Skip to content

The value of an application

Event: an application’s sections are complete and its value is wanted right away, to report to the ad platform. Endpoint: POST /v1/score Status: live in dev. Replaces POST /score on the EV Worker (vbb.beeline.team): same body, same answer, plus the fields Stitch adds.

Who sends this. Stitch does not know and does not need to. It sees a key and a payload. Today the likeliest caller is whatever holds the application’s figures at the point its sections are complete — most likely the application’s own backend; a middleware that fronts the application sends exactly the same thing. How that system knows the sections are complete is its business.

Authorization: Bearer <your key>

One key per emitting system, named by the owner after whatever actually sends. A missing or wrong key is 401.

fieldrequirednotes
loan_amountyesdollars; the only field whose absence is a 400
property_valuestrongly recommendeddollars, as the applicant typed it (purchase price for purchases); the round-number signal lives here
credit_rangerecommendedthe form’s stated range, either generation ("Above 740", "Between 700 To 719", …) or a number
ficooptionalthe pulled score; when present the FICO model is used
loan_purposerecommendedPurchase, Refi - CO, Refi - RT; aliases accepted (cash-out refinance, rate/term)
product_groupingrecommendedDSCR, Conv, Bank Stmt/Non-QM, Govt/Jumbo/Other; aliases accepted (conventional, bank statement, FHA)
occupancyoptionalaccepted; not used by the shipped models

Unknown labels do not fail the request: the field falls back to its reference level and a warning is returned.

Terminal window
curl -s -X POST https://stitch-collector-dev.magic-blocks.workers.dev/v1/score \
-H 'content-type: application/json' -H 'authorization: Bearer <your key>' \
-d '{"loan_amount":250000,"property_value":357143,"credit_range":"Above 740","loan_purpose":"Refi - CO","product_grouping":"DSCR"}'
{
"prediction_id": "3f2c…", "ev": 4310.82, "conversion_value": 4310.82,
"p_close": 0.4134, "rev_if_close": 10428.1,
"model_used": "credit_range", "model": "app_ev_v4_5field_claimed",
"trained_on": "2026-10-21", "train_window": ["2025-10", "2026-08"],
"calibration_multiplier": 1, "calibration": { "manual": 1, "auto": 1, "auto_source": "off" },
"inputs_used": { "loan_amount": 250000, "property_value": 357143, "ltv": 0.7, "credit_range_group": "740+", "...": "..." },
"warnings": [], "complete": true,
"ev_cents": 431082, "conversion_value_cents": 431082, "rev_if_close_cents": 1042810
}

Report conversion_value as the value of the application conversion you report to Google. auto_source is always off: Stitch never recalibrates automatically; the level correction is set by an admin on the Value model screen and recorded on every prediction.

  • Nothing is logged by this endpoint. The predictions that count are the ones Stitch writes against loans as their stage events arrive (loan-stages.md), with their model version, calibration and inputs; outcomes join by loan, so prediction_id is returned for compatibility and has nothing to join to.
  • POST /outcome, GET /drift, GET /export and GET /calibration have no equivalent here: outcomes are the funded stage events, drift is reported by channel on the Value model screen, and the refit reads Stitch’s prediction export (GET /api/export/predictions?since=YYYY-MM-DD on the app, with the export token).

What moves the value afterwards, and what does not

Section titled “What moves the value afterwards, and what does not”

A loan is scored once, at the first stage event that finds it at app_submitted or beyond, from the latest attributes the file has by then. It is re-scored for one reason: the pulled score (fico or credit_score) arriving on a later event, which switches to the FICO model — and that re-score reads the other inputs as the first score read them (recorded with it as the loan attribute ev_raw_inputs), plus the score; a key with no score in it (fico: null) is not the score arriving. A later loan_amount or property_value on the file is an edit to the file, not the borrower’s entry, so it re-scores nothing and restates nothing; it is recorded as the loan’s attributes and exported beside the scored inputs as final_loan_amount, final_property_value and loan_amount_change for the refit. The value reported to Google is therefore always the one computed from what the borrower entered, adjusted afterwards only by the re-score, the realised revenue at funding, or the $1 floor at a dead end — each a restatement Stitch sends itself (outbound.md).