score is an explainable roll-up over the returned signals: it is reproducible from the
signals shown plus the published table below. Phase 1 is a transparent rule engine with
equal weight for every signal, so the base score is the mean of the resolved contributions.
The published table
The table is versioned data, published alongside the scorer. A score is reproducible from thesignals shown plus this table; calibrating the numbers is a data change and a version bump,
not a silent re-scoring.
The response does not carry the table version today —
data[].{score, band, reason, signals, input, correlation_id} is the whole scored item — so recompute against the version
published here.Band overrides and reason
A dominant check can override the band and set the reason. When several apply, the first one
here sets the reason:
- any
*_name_match=false→reason: name_mismatch, band forced tohigh_risk; - any
*_profile_match=false→reason: profile_contradiction, band forced tohigh_risk; phone_connectivityorpremium_phone_connectivity=false→reason: phone_disconnected(band from the score);phone_type=falseorphone_ported=false→reason: line_risk(band from the score).
reason only. The band is the most severe band named by any
override that fired, and only where that band is more severe than the score-derived one. A
false *_name_match therefore lands high_risk even when another row publishes the reason.
If no override fires, reason is the lowest-scoring resolved signal, named by its signal key.
Recomputing a score
Take thesignals map, turn each resolved verdict into a contribution with the table, average
them, round with the half_up rule, then apply the overrides.
Worked example — a consistent identity
The requested email resolves to no one, soemail_name_match is null and drops out of the
denominator.
Result (GET /v1/signals/rq_7a1d)
50 + 90/2 = 95, 50 + 98/2 = 99, 50 + 76/2 = 88;
email_name_match is null and excluded. Mean (95 + 99 + 88) / 3 = 94 → score: 94,
band clear (≥ 75). No override fired, so reason is the lowest-scoring resolved signal,
phone_profile_match (88).
Worked example — a name mismatch
The phone is live but belongs to a different name and profile, soname_mismatch fires and
forces high_risk.
Delivery
50 − 100/2 = 0, 50 + 94/2 = 97, 50 − 78/2 = 11. Mean
(0 + 97 + 11) / 3 = 36. phone_name_match is false, so name_mismatch fires and forces
high_risk regardless of the score → score: 36, band high_risk, reason name_mismatch.