Artisan Commands
Reference for every custom artisan command across the FlexPoint Portal API and the FlexRate pricing engine, with flags and when to run them.
Every first-party artisan command in the two Laravel services, grouped by
purpose. Signatures are transcribed verbatim from each command's
protected $signature.
Run from the right app root
flex-hub-api commands run from apps/flex-hub-api;
flex-rate-api commands run from apps/flex-rate-api.
Both are separately-deployed services with independent artisan binaries.
FlexPoint Portal API (apps/flex-hub-api)
Provisioning & identity sync
flexpoint:provision
Bootstrap a fresh instance: seed roles/notifications synchronously, then dispatch the MLM identity imports onto their priority queues. See the full Provisioning runbook.
php artisan flexpoint:provision --force| Flag | Effect |
|---|---|
--skip-mlm | Seed identity + notifications only; do not dispatch MLM import jobs. |
--force | Run without the production confirmation prompt (required in CI / non-interactive deploys). |
mlm:sync-originating-companies
Sync originating companies from MLM into the portal database. Internally queues
staggered ImportExternalUsers jobs for every company it discovers.
php artisan mlm:sync-originating-companies| Flag | Effect |
|---|---|
--now | Run the sync immediately instead of queueing it. |
mlm:sync-internal-users
Sync internal (employee) users from MLM into the portal database.
php artisan mlm:sync-internal-users| Flag | Effect |
|---|---|
--email=* | Only import the employee(s) with these exact email addresses (repeatable). |
--now | Run the sync immediately instead of queueing it. |
mlm:sync-external-users
Import external (PML) users from MLM for active originating companies.
php artisan mlm:sync-external-users| Flag | Effect |
|---|---|
--company= | Only import users for the originating company with this index number. |
--now | Run the imports immediately instead of queueing them. |
MLM freshness
mlm:rehydrate-active-loans
Re-hydrate active imported loans from MeridianLink — the interim freshness sweep that keeps mapped fields and collections current without waiting on webhooks.
php artisan mlm:rehydrate-active-loans| Flag | Effect |
|---|---|
--loan= | Only rehydrate the loan with this MLM number / reference. |
--all | Include terminal-stage loans (funded / denied / withdrawn) too. |
--stagger=5 | Seconds between successive queued jobs. |
--now | Run the rehydrations immediately instead of queueing them. |
mlm:resync-leads
Re-sync leads from MeridianLink — backfills multi-consumer ownership links and refreshes lead fields.
php artisan mlm:resync-leads| Flag | Effect |
|---|---|
--lead= | Only resync the lead with this MLM number / reference / id. |
--all | Include terminal-stage leads (funded / denied / withdrawn) too. |
--stagger=5 | Seconds between successive queued jobs. |
--now | Run the resyncs immediately instead of queueing them. |
Discovery & audit
Live vendor calls
mlm:audit-loan, mlm:dump-collection, and
mlm:probe-register hit MeridianLink under a real service ticket by
default. Use --cached / --dry-run where available for
read-only work, and never run the probe against a production loan.
mlm:audit-loan
Diff the LOS payload against the portal field maps to surface unmapped data — the machine answer to "is every data element imported?".
php artisan mlm:audit-loan <reference>| Argument / Flag | Effect |
|---|---|
reference | The MLM loan number / reference (sLNm or sLRefNm). |
--cached | Diff the stored snapshot instead of fetching a fresh one. |
--json | Emit the gap report as JSON. |
mlm:audit-status-parity
Audit read + write parity for the in-scope MLM status/date fields (16 Key Dates +
status): every field must have a MlmLoanFieldMap spec or a documented unmapped
entry.
php artisan mlm:audit-status-paritymlm:audit-visibility
Audit loan/lead visibility: how many grants rely on the vendor-number/ref backstop versus the stable FK.
php artisan mlm:audit-visibility| Flag | Effect |
|---|---|
--samples=10 | Max sample backstop-reliant rows to list per side. |
mlm:dump-collection
Dump a MeridianLink LoadByRefNumber collection for a loan as JSON — the field mapping discovery helper.
php artisan mlm:dump-collection <loanNumber> <collection>| Argument / Flag | Effect |
|---|---|
loanNumber | The MLM loan reference number (sLRefNm). |
collection | The MLM collection id, e.g. Consumers, Assets, ConsumerAssetOwnerships. |
--field=* | Record field ids to request (repeatable). Defaults apply for known collections. |
--out= | Write the JSON to this path (relative to the app root) instead of stdout. |
mlm:xml-to-json
Convert a MeridianLink LOXmlFormat loan XML file into a flat JSON object keyed
by field id — for offline transform and fixture capture.
php artisan mlm:xml-to-json <path>| Argument / Flag | Effect |
|---|---|
path | Path to the LOXmlFormat XML file (relative to the app root or absolute). |
--out= | Write the JSON to this path instead of stdout. |
--compact | Emit minified JSON instead of pretty-printed. |
mlm:probe-register
Live probe: register (and optionally lock) a program on a test loan and diff what MLM sets — used to reverse-engineer the rate-lock contract.
php artisan mlm:probe-register <loan> --dry-run| Argument / Flag | Effect |
|---|---|
loan | MLM loan number / reference (sLNm or sLRefNm). |
--ticket= | Explicit MLM session ticket (else mints one from config creds). |
--internal | Mint an internal-employee ticket instead of a broker (PML) ticket. |
--program= | Target program name (else the first eligible program from PriceMyLoan). |
--template= | Target lLpTemplateId (else the picked rate option template). |
--rate= | Target note rate, e.g. 6.99 (else the picked rate option rate). |
--fee= | requestedFee / points, e.g. -0.125 (else the picked rate option point). |
--lock | Also call LockLoanProgram after a successful register. |
--skip-dup | Use the skip-duplicate-check register op (safe to re-run on a registered loan). |
--list | Price the loan and print all eligible program/rate options, then exit. |
--write-dates | Probe finalize: write sRateLockStatusT=2 + sRLckdD + sRLckdExpiredD via Save, then diff. |
--lock-days=30 | Lock period in days passed to register / lock. |
--dry-run | Read-only: mint ticket, price, and snapshot fields, but do not register/lock. |
Maintenance & retention
mlm:prune-webhook-receipts
Delete MLM change-webhook receipts older than the retention window to keep the audit table bounded.
php artisan mlm:prune-webhook-receipts| Flag | Effect |
|---|---|
--days=7 | Delete receipts received more than this many days ago. |
mlm:purge-document-bytes
Delete pre-downloaded MLM e-doc bytes (mlm-documents/) to reclaim disk;
metadata and live streaming are unaffected.
php artisan mlm:purge-document-bytes --dry-run| Flag | Effect |
|---|---|
--dry-run | Report what would be deleted without deleting anything. |
--force | Skip the confirmation prompt. |
Notifications & realtime
notifications:scan
Fire scheduled notification triggers for overdue loan conditions (loans with underwriting conditions past due and still unsatisfied).
php artisan notifications:scanreverb:smoke
Post-deploy smoke test that verifies the Reverb WebSocket server is accepting connections. See Realtime (Laravel Reverb).
php artisan reverb:smoke| Flag | Effect |
|---|---|
--internal | Check the internal server bind instead of the public host. |
--timeout=5 | Connection timeout in seconds. |
FlexRate pricing engine (apps/flex-rate-api)
Rate-sheet extraction
Each extractor turns a lender's XLSX rate sheet into JSON. Defaults read from
storage/app/incoming/<lender>-latest.xlsx and write to
storage/app/rates/<lender>/.
rates:extract-flexpoint
Extract the FlexPoint Wholesale rate sheet xlsx into JSON per product tab.
php artisan rates:extract-flexpoint --pretty| Flag | Effect |
|---|---|
--file= | Path to the FlexPoint rate sheet xlsx (default storage/app/incoming/flexpoint-latest.xlsx). |
--out= | Output directory (default storage/app/rates/flexpoint). |
--pretty | Pretty-print the JSON output. |
rates:extract-non-qm
Extract the Non-QM rate sheet xlsx into one JSON document per program (Full Doc, DSCR, Jumbo, …).
php artisan rates:extract-non-qm --pretty| Flag | Effect |
|---|---|
--file= | Path to the Non-QM rate sheet xlsx (default storage/app/incoming/non-qm-latest.xlsx). |
--out= | Output directory (default storage/app/rates/non-qm). |
--pretty | Pretty-print the JSON output. |
rates:extract-silver-hill
Extract the Silver Hill rate sheet xlsx (Multi Property DSCR program) into JSON.
php artisan rates:extract-silver-hill --pretty| Flag | Effect |
|---|---|
--file= | Path to the Silver Hill rate sheet xlsx (default storage/app/incoming/silver-hill-latest.xlsx). |
--out= | Output directory (default storage/app/rates/silver-hill). |
--pretty | Pretty-print the JSON output. |
Matrix PDFs
rates:matrix-fetch
Download the Non-QM matrix PDFs, cache raw pdftotext output, and write the
fetch manifest (with SHA256 checksums).
php artisan rates:matrix-fetch| Flag | Effect |
|---|---|
--force | Re-download PDFs even when the local file already exists. |
--dest= | Override destination directory for downloaded PDFs. |
rates:matrix-extract
Parse the fetched Non-QM matrix PDFs and write per-program eligibility JSON.
php artisan rates:matrix-extract --pretty| Flag | Effect |
|---|---|
--program= | Extract only the named program slug. |
--pretty | Pretty-print output JSON. |
Daily orchestration
rates:daily
Run the full daily rate-sheet ingest: extract both xlsx files, fetch + parse the matrix PDFs, and reseed the rates database.
php artisan rates:daily| Flag | Effect |
|---|---|
--non-qm-file= | Override path to the Non-QM xlsx. |
--flexpoint-file= | Override path to the FlexPoint xlsx. |
--skip-matrix | Skip matrix PDF fetch and extraction. |
Parity harness
The parity harness proves FlexRate pricing matches MeridianLink across every program and LLPA dimension.
parity:generate-matrix
Generate the FlexRate ↔ MLM parity scenario matrix (the manifest of every MLM form submission needed to prove parity).
php artisan parity:generate-matrix| Flag | Effect |
|---|---|
--out= | Override output path (default parity/scenarios/matrix.json). |
--limit= | Truncate the manifest to N scenarios (smoke testing). |
parity:capture
Drive MLM through the parity matrix via the Playwright sidecar, writing captured
observations to parity/observations/<run-id>/.
php artisan parity:capture --run-id=<id>| Flag | Effect |
|---|---|
--run-id= | Run identifier; observations written to parity/observations/<run-id>/. |
--auth | Run the one-time login helper instead of the capture loop. |
--scenario= | Run a single scenario by id (smoke testing). |
--matrix= | Override scenario manifest path (default parity/scenarios/matrix.json). |
--limit= | Process at most N scenarios this run (resumable). |
--force | Re-capture scenarios whose output file already exists. |
--headless | Run Chromium headless (default: visible window). |
--fail-fast | Abort the run on the first scenario failure. |
--pause-after-fill | Fill the form then pause (keeps browser open for DevTools); does not submit. |
parity:compare
Diff captured MLM observations against in-process FlexRate pricing and produce per-scenario diff reports.
php artisan parity:compare| Flag | Effect |
|---|---|
--run-id= | Run ID (default: the most recent observations subdirectory). |
--matrix= | Path to scenario manifest (default parity/scenarios/matrix.json). |
--tolerance=0.0001 | Pricing tolerance in points / price units. |
parity:ingest-soap
Convert a BFP-API SOAP live-capture into a parity observation JSON.
php artisan parity:ingest-soap <scenario> <bfp_capture_dir>| Argument / Flag | Effect |
|---|---|
scenario | Our scenario id (e.g. pp-flexpoint-homeready). |
bfp_capture_dir | Path to the BFP API live-capture dir containing response.parsed.json. |
--run-id=smoke | Output run id (default smoke). |
Tooling
pricer:postman-export
Build a Postman v2.1 collection (and environment) from PHPUnit capture fixtures for manual API verification.
php artisan pricer:postman-export| Flag | Effect |
|---|---|
--source= | Override capture directory (default tests/fixtures/postman/captured). |
--out= | Override output directory (default docs/postman). |