Skip to content

Fraud signals

Suspicion is a first-class, structured output of this system — not a buried log line. Every signal becomes a human-readable entry in decision_reasons, an event in the audit chain, and an input to the decision engine. This page is the catalogue.

The engine applies signals in severity order. The rule underneath everything: suspicion never auto-approves.

OutcomeTriggered by
rejected (hard fail)Not an ID document · expired document · failed liveness · face match below threshold
manual_review (suspicion or uncertainty)Any tamper signal · MRZ check-digit failure · MRZ↔visual disagreement · low AI confidence · missing core fields · liveness not assessable · biometric stack unavailable · any pipeline error
approvedZero signals, all checks affirmatively passed
Signal (as seen in decision_reasons)What it meansWhy it matters
MRZ validation failed: document number check digit invalid (also DOB / expiry / composite)The ICAO 9303 check digits printed on the document don’t validateForgers who alter printed data rarely recompute the checksums — this is cheap, deterministic maths catching real tampering
MRZ/visual disagreement: DOB mismatch … (also expiry, family name)The machine-readable zone and the printed text disagreeClassic photo-substitution / partial-alteration signature: one zone edited, the other forgotten
tamper signal: … (free text from AI inspection)The vision model saw concrete artifacts: photo-substitution edges, font/kerning mismatches, digital editing traces, screen re-capture moiréThe broad net for forgeries the deterministic checks can’t see
document expired on YYYY-MM-DDHard rejectExpired documents are never acceptable ID
uploaded image is not an identity documentHard rejectCat photos, memes, blank paper
low extraction confidence (0.xx)AI couldn’t read the document confidentlyUsually capture quality; occasionally deliberate obfuscation
quality: …Blur, glare, cropping, occlusionSame — and a fraud vector (hide the tampered region under glare)
SignalWhat it means
liveness check failed + step 'left': head yaw NN° outside expected rangeThe “person” didn’t perform the requested pose — static photo, replay, or puppet
identity changed between frames (consistency 0.xx)A different person appeared mid-challenge — the classic “accomplice does the turns, fraudster does the match frame” attack
step 'x': expected exactly 1 face, found NNobody, or multiple people, in frame
face match below threshold (0.xx)The selfie is not the person on the document — stolen-document signature
no face found on the documentPortrait unreadable or absent — quality or tampering
no active challenge issued — liveness not assessableSingle-frame fallback path was used; match may pass but liveness can’t → review
expected N challenge frames, got MDevice sent an incomplete challenge — abandoned flow or a client trying to skip steps

The audit chain gives you the raw material for velocity/pattern detection that the per-verification pipeline can’t see:

  • The same external_user_ref creating many verifications after rejections (retry-until-lucky).
  • Bursts of verifications from one API key outside its product’s normal traffic shape (leaked key).
  • Many manual_review outcomes sharing one tamper-signal phrasing (a forgery template circulating — escalate per incident response).
  • 429s from the capture endpoints (automated abuse probing).

Alerting recipes for all of these: Monitoring.

Current defaults, and the trade-off each controls:

KnobDefaultRaising itLowering it
Face match threshold0.40 cosineFewer impostors pass; more twins/aging/photo-quality false rejectsOpposite
Frame consistency0.55 cosineStricter same-person enforcement across the challengeTolerates more pose/lighting variation
AI confidence floor0.85More reviews, fewer misreads acted onFewer reviews
Pose yaw windows±15° center · 18–90° turnsStricter pose proofEasier UX

Calibrate against your population before trusting auto-approve: run 10–20 known- good real ID + selfie pairs and a handful of deliberate spoofs (printed photo, phone screen replay), and check both sides of each threshold. Re-run whenever demographics or capture hardware shifts materially.