Reason codes
Stable machine codes explaining why a check did not approve
A reason code says why a check produced something other than approve. They are stable, upper snake
case strings, they appear on the check that produced them and on the verification as a whole, and
they are the right thing to branch on when you decide what to tell a user.
They are additive. New codes can appear at any time, so always have a default branch that shows generic guidance rather than throwing on an unrecognised code.
Selfie and liveness
| Code | Meaning |
|---|---|
BAD_VIDEO_SELFIE | The recorded selfie could not be used. Too dark, too blurred, or the face left the frame. |
BAD_SELFIE | The still selfie could not be used, for the same kinds of reason. |
CAMERA_OR_LIGHTING_ISSUES | The capture conditions defeated the check rather than the person. |
LIVENESS_GESTURE_FAILED | The person did not complete the movement they were asked to perform. |
FACE_NOT_DETECTED | Too few of the captured frames contained a face to judge. |
FRAUDULENT_LIVENESS | The capture evidenced a presentation attack rather than a live person. |
VIDEO_FILTERS_OR_MASKS | A filter or a mask was altering the face during capture. |
Face match
| Code | Meaning |
|---|---|
BAD_FACE_MATCHING | The selfie did not match the portrait on the document that was presented. |
FACE_MATCH_LOW_CONFIDENCE | Inconclusive. The comparison came close to the line without passing it. |
SELFIE_MISMATCH | The selfie appears to be a different person from the document. |
PERSON_MISMATCH | The person in front of the camera is not the person on the document. |
Document
| Code | Meaning |
|---|---|
EXPIRATION_DATE | The document is past its expiry date. |
EXPIRY_UNREADABLE | No expiry date could be read, so the document cannot be confirmed as in date. |
PROPOSED_NOT_SETTLED | We re-checked this session as maintenance. The result is waiting on a reviewer rather than settled. |
DOCUMENT_EXPIRY_EXTENDED | Past its printed expiry, but carrying an official extension. |
MRZ_CHECKSUM_INVALID | The machine-readable zone failed its own integrity check. |
ID_INVALID | The document failed a structural validation: a number failed its check, or the printed page disagrees with the machine-readable zone. |
INCOMPLETE_DOCUMENT | A field genuine documents of this kind always carry is missing or fails its own check, for instance a masked national number. Learned per country and document type from reviewed documents. |
OCR_LOW_CONFIDENCE | Readable enough to process, not clean enough to be sure. |
UNKNOWN_DOCUMENT_LAYOUT | The layout is one we have no specific reader for, so fields were read generically. |
DATE_ORDER_SUSPECT | The printed dates cannot all be right, for instance issued after expiry. |
NOT_DOCUMENT | What was submitted is not a document we recognise. |
DOCUMENT_MISSING | A required document was not supplied. |
LOW_QUALITY | The image is too poor to read reliably. |
OUTDATED_DOCUMENT_VERSION | A version of the document the issuer no longer produces. |
Re-presentation and tampering
| Code | Meaning |
|---|---|
SCREENSHOTS | The file is a screenshot rather than a photograph taken with a camera. |
SCREEN_RECAPTURE | A photograph of a screen showing the document, rather than of the document. |
GRAPHIC_EDITOR | The file carries the signature of image-editing software. |
TAMPERING_TRACE | Compression analysis found traces consistent with editing. |
DIGITAL_DOCUMENT | A digital rendering rather than a photograph of a physical document. |
DOCUMENT_TEMPLATE | The image matches a specimen document the issuer publishes. |
FORGERY | The document is not genuine. |
One person, more than once
These say the verification is a repeat rather than that anything is wrong with the document. All three ask for a human and none of them rejects anybody on its own.
| Code | Meaning |
|---|---|
FRAUD_NETWORK_LINK | This person is linked to another applicant record. |
DUPLICATE_DOCUMENT_IMAGE | The same image file was submitted under two different applicants. |
DUPLICATE_DOCUMENT_IN_FLIGHT | The same document is on another verification of yours that is still open, under a different reference_id. |
SHARED_DEVICE | Several applicants used the same browser profile. |
DUPLICATE_DOCUMENT_IN_FLIGHT is worth knowing about, because it fills a window nothing else can
see. If someone has already been approved, a second attempt is refused outright and you get
ALREADY_VERIFIED. But two verifications started at the same time are both undecided, so neither can
refuse the other, and both reach your queue looking like unrelated applicants. This code is what says
they are not.
It only appears when the two carry different reference_id values. One person retrying their own
verification is ordinary and never flagged.
Two more codes close a verification outright instead of asking for a human. Both appear on a session
with status: "expired", and neither is a judgement about the person: they mean this session
produced no new verification.
| Code | Meaning |
|---|---|
ALREADY_VERIFIED | You already hold an approval for this person. Either the attempt was refused, or it was an open review case that another approval took off the queue. |
REVIEW_IN_PROGRESS | This person already has a verification waiting on manual review, and this attempt was refused until that case is decided. |
When a review case is closed by another approval, ALREADY_VERIFIED is added to the codes it already
carried rather than replacing them, so you can still see why it went to review. The whole behaviour
is described in Check existing applicants.
Applicant details
| Code | Meaning |
|---|---|
REQUESTED_DATA_MISMATCH | The details you sent do not match what the document says. |
PROBLEMATIC_APPLICANT_DATA | The supplied details are internally inconsistent or implausible. |
DB_DATA_MISMATCH | The details did not match the registry that was checked. |
DB_DATA_NOT_FOUND | The registry that was checked holds no record for these details. |
INCORRECT_SOCIAL_NUMBER | The national number does not match the rest of the identity. |
Compliance
| Code | Meaning |
|---|---|
AGE_REQUIREMENT_MISMATCH | The person's age falls in a range the flow refuses, at either end. |
AGE_OUTSIDE_ACCEPTED_RANGE | The person's age falls in a range the flow sends to a reviewer. |
AGE_NOT_DETERMINED | The flow states an age requirement and no date of birth could be read. |
SANCTIONS | The identity matched sanctions or watchlist data. |
PEP | The identity matched politically exposed person data. |
ADVERSE_MEDIA | The identity matched adverse media data. |
CRIMINAL | The identity matched criminal record data. |
RESTRICTED_PERSON | The person may not be onboarded. |
WRONG_USER_REGION | The person is not somewhere the flow serves. |
Nothing concluded
These do not say anything about the person. They say a check could not reach an answer, which is why they never appear alone on an approval.
| Code | Meaning |
|---|---|
CHECK_UNAVAILABLE | The check could not run. |
CAPTURE_MISSING | The capture the check needed was not there. |
CHECK_SKIPPED | The check was not run for this verification. |
When your own policy decided
| Code | Meaning |
|---|---|
POLICY_WAIVED | The verdict was softened by an acceptance rule you configured. |
POLICY_WAIVED is added, never substituted. The findings that were waived stay on the check and on
the verification, so a record always says both what was found and what you decided about it. If you
see an approval carrying reason codes, this is why.
Turning codes into copy
Codes fall into two groups, and the distinction matters more than the individual code.
Some describe a capture problem, where the person is probably fine but the submission was not.
BAD_VIDEO_SELFIE, OCR_LOW_CONFIDENCE and FACE_MATCH_LOW_CONFIDENCE are all in this group.
Invite the user to try again with specific guidance, better light, a flat surface, no glare.
Others describe a substantive failure, where retrying will not help. AGE_REQUIREMENT_MISMATCH
and SANCTIONS are in this group. Do not invite a retry, and be careful about how much detail you
reveal, since telling someone precisely which screening list they matched is rarely appropriate.
Do not echo raw codes to users
Reason codes are machine strings for your logic, not user-facing copy. Map them to your own wording, in your own tone, in the user's language.