When a verification needs a person, the API tells you why. It does so at two levels, with a closed set of categories and a published list of codes. This page lists every code, its category, and the sentence the API returns as its message.
Two levels
Section titled “Two levels”- Per-check reasons.
check_results[].reasonsonGET /v1/sessions/{id}says why one check needs a person, was only partly performed, or could not produce a result. Each reason is acode, acategoryand amessage, with acountwhen the reason counts items. - Session-level causes.
review.causesonGET /v1/sessions/{id}, and on each item ofGET /v1/sessions, says why the whole verification was sent to a reviewer. Each cause is acodeand amessage, with arule_namewhen the cause is a rule you wrote.
The two levels answer different questions. A check can carry reasons on a verification that was never sent to review. A verification can be sent to review by a cause that no check explains, for example one of your own rules.
{ "data": { "id": "vs_01H...", "status": "awaiting_review", "outcome": "review", "decided_at": null, "review": { "causes": [ { "code": "check_needs_review", "message": "One or more checks need a reviewer to confirm." }, { "code": "rule_triggered", "rule_name": "Out-of-state ID", "message": "Your rule \"Out-of-state ID\" sent this verification to review." } ] }, "checks": ["watchlist", "id_verification"], "check_results": [ { "type": "watchlist", "outcome": "review", "reasons": [ { "code": "record_match_needs_confirmation", "category": "needs_judgment", "count": 2, "message": "A search found a record with details that match the applicant. Two different people can share a name or a date of birth." } ] }, { "type": "id_verification", "outcome": "review", "reasons": [ { "code": "expiry_date_passed", "category": "needs_judgment", "message": "The expiration date on the document has already passed." } ] } ] }}The three categories
Section titled “The three categories”A reason’s category is a closed list of three values. It is what to branch on when you meet a reason code you do not recognize.
category |
What it means | Label on a part of an identity check |
|---|---|---|
needs_judgment |
A person has to look at the result. | Needs review |
not_checked |
Part of this check was not performed, for example a selfie comparison in a workflow with no selfie step. It never appears alone. | Not checked |
did_not_finish |
The check could not produce a result. | Couldn’t complete |
The third column is the label the reviewer screen gives one part of an identity check, such as a document sub-check or the selfie comparison. A whole check’s label follows its outcome, not the category of its reasons: a refused e-signature reads Needs review on the reviewer screen although its reason is did_not_finish.
Per-check reasons
Section titled “Per-check reasons”A check carries reasons when its own outcome is review, fail or error, except the ID verification entries described below: an earlier run of a re-verified step and, in a workflow with two ID verification steps, the step that does not get them. This does not depend on the verification being sent to review. For example, an applicant who refuses to sign completes the verification as declined with no review, and the e-signature check still carries applicant_declined_to_sign.
- A check that passed, or has not concluded, has no
reasons. - A check with reasons always has at least one reason in
needs_judgmentordid_not_finish. When nothing more specific is known, that reason ischeck_needs_confirmationfor a check atreview, orcheck_did_not_finishfor a check atfailorerror. - Each code appears once per check.
- A check that was re-verified appears once per run, latest run first. For a re-verified ID verification step, only the latest run carries
reasonsand an earlier run carries none. An earlier run of any other check type keeps the reasons it had. - A workflow with two ID verification steps gives
reasonsto one of them: the step re-verified most often, or, when that is tied, the step later in the workflow. The other step’s entries carry none, even when their outcome isreview,failorerror. - An ID verification code takes its category from what happened to the part of the check it describes:
needs_judgmentwhen that part was sent for review,not_checkedwhen that part was skipped. Each code is recorded one way, exceptdocument_type_unrecognized: two parts of the check report it in the same check, one by skipping and one by sending it for review. A code appears once per check, under the stronger category, so it is alwaysneeds_judgment. selfie_match_not_runis expected on a flagged ID check when the workflow has no selfie step. It is not a failure. Like everynot_checkedreason, among themsecond_side_not_read,images_not_analyzed,name_check_not_runandname_check_did_not_complete, it says what was not done, not why the check was flagged. So it never appears alone: a reason in another category is always beside it, and that reason is the one that explains the flag.record_match_needs_confirmationcarries acount: the number of records attached to the check that a reviewer is asked to look at. The reason does not name the list, the authority or the kind of match.- Credit history reasons appear here too, with the same codes as
credit.review_reasons, so one array covers every check type.
| Code | Where it applies | Category | Message |
|---|---|---|---|
document_type_unrecognized |
ID verification | needs_judgment |
We could not identify what kind of document this is. |
document_type_not_accepted |
ID verification | needs_judgment |
This kind of document is not one this verification is set to accept. |
expiry_date_passed |
ID verification | needs_judgment |
The expiration date on the document has already passed. |
expiry_date_unreadable |
ID verification | needs_judgment |
No expiration date could be read from the document. |
second_side_not_read |
ID verification | not_checked |
The two sides of the document were not compared against each other, so no result was recorded. |
document_sides_disagree |
ID verification | needs_judgment |
The information on the two sides of the document does not agree. |
date_of_birth_unreadable |
ID verification | needs_judgment |
No date of birth could be read from the document, so the age could not be confirmed. |
below_minimum_age |
ID verification | needs_judgment |
The date of birth on the document puts the applicant below the minimum age this verification requires. |
images_not_analyzed |
ID verification | not_checked |
The captured images could not be analyzed for signs of a photo of a photo or a screen. |
capture_looks_reproduced |
ID verification | needs_judgment |
The capture shows signs of being a photo of a photo, or of a screen. |
name_check_not_run |
ID verification | not_checked |
The name comparison could not be started, so no result was recorded. |
name_check_did_not_complete |
ID verification | not_checked |
The name comparison started but did not finish, so no result was recorded. |
name_does_not_match |
ID verification | needs_judgment |
The name on the document does not match a name on file for the applicant. |
booking_name_does_not_match |
ID verification | needs_judgment |
The name on the document does not match the name on the booking. |
bureau_flagged_for_review |
Credit history | needs_judgment |
The credit report came back marked for review. |
no_credit_file_returned |
Credit history | needs_judgment |
No credit file was returned. |
credit_score_below_minimum |
Credit history | needs_judgment |
The score is below the minimum this check requires. |
selfie_does_not_match_document |
ID verification | needs_judgment |
We couldn’t automatically confirm the document and selfie belong to the same person. |
selfie_match_not_run |
ID verification | not_checked |
The selfie was not compared with the document photo. |
selfie_match_did_not_complete |
ID verification | did_not_finish |
The selfie comparison did not complete, so no result was recorded. |
liveness_not_confirmed |
ID verification | needs_judgment |
Liveness was not confirmed for the selfie capture. |
liveness_did_not_complete |
ID verification | did_not_finish |
The liveness check did not complete, so no result was recorded. |
document_unreadable |
ID verification | did_not_finish |
The document’s printed information could not be read for this check. This can happen with an unclear image or a worn document. Review the captured images, or ask the applicant to recapture a clear photo of the document. |
document_not_detected |
ID verification | did_not_finish |
No document was found in the submitted image. The applicant may need to recapture with the full document visible in the frame. |
image_quality_too_low |
ID verification | did_not_finish |
The image did not meet the quality threshold for this check, for example low resolution, compression, motion blur, glare, or darkness. A clearer, steadier recapture is needed. |
wrong_document_side |
ID verification | did_not_finish |
The incorrect side of the document was submitted. The applicant should recapture using the required face of the document. |
document_back_missing |
ID verification | did_not_finish |
The back of the document was not submitted. Both sides are required for this check. |
document_not_supported |
ID verification | did_not_finish |
The document type or issuing country is not supported by this check. The applicant should provide a supported document. |
document_encoded_data_unreadable |
ID verification | did_not_finish |
The machine-readable data on the document (its code strip or barcode) could not be read for this check. This can happen with a worn, damaged, or unsupported document. Review the captured images, or ask the applicant to recapture or provide a supported document. |
check_result_unreadable |
Any check | did_not_finish |
This check couldn’t be completed because the result was in an unexpected format. It’s been logged and can be run again. |
check_unexpected_error |
Any check | did_not_finish |
This check couldn’t be completed because of an unexpected error. It’s been logged and can be run again. |
applicant_name_missing |
Any check | did_not_finish |
This check didn’t run because the applicant has no name on record that can be screened. A reference label is not a name. Add the applicant’s name, either as given names plus family name or as a whole legal name, then run the check again. |
applicant_date_of_birth_missing |
Any check | did_not_finish |
This check didn’t run because the applicant has no date of birth on record. A background check needs one to tell people with the same name apart. Add the applicant’s date of birth, then run the check again. |
check_not_available_in_region |
Any check | did_not_finish |
This check couldn’t run in this region yet. It’s been logged and can be run again. |
check_not_set_up_in_region |
Any check | did_not_finish |
This check is not set up to run in this region yet, so it was never ordered. It’s been logged, and it can be run again once it’s available. |
check_ended_without_result |
Any check | did_not_finish |
This check ran but ended without a result. It can be run again. |
check_timed_out |
Any check | did_not_finish |
This check ran but didn’t finish in the time allowed. It can be run again. |
form_needs_review |
Custom form | needs_judgment |
This form was sent to review. |
applicant_declined_to_sign |
E-signature | did_not_finish |
The applicant read the document and refused to sign it. |
record_match_needs_confirmation |
A check that searches records | needs_judgment |
A search found a record with details that match the applicant. Two different people can share a name or a date of birth. |
check_needs_confirmation |
Any check | needs_judgment |
This check needs a person to confirm before you decide. |
check_did_not_finish |
Any check | did_not_finish |
This check didn’t finish, so no result was recorded. It can be run again, or you can decide on the checks that did complete. |
form_needs_review does not say that one of your form rules fired. A form is also sent to review when its answers arrive for the wrong step, and the stored result does not tell the two apart.
Session-level causes
Section titled “Session-level causes”review is present when the verification was sent to a reviewer: its status is awaiting_review, or its latest run sent it to review. It is absent on a verification that was never sent to review. The causes describe the latest run.
causesis never empty. When nothing more specific can be said, it holdsreview_required.- The order is stable.
processing_errorcomes first, thencheck_needs_review, then the signal causes in the order of the table below, thenrule_triggered, thenreview_policy.review_requiredappears only when no other cause does. rule_triggeredappears once for each distinct rule name, with that name inrule_name. All the rules whose name cannot be found produce one entry with norule_name, and that entry has the name-free message.- A rule whose action is to decline is reported as
rule_triggered, the same as a rule that sends to review. A rule never declines a verification on its own: it sends it to a person. The cause says that a rule fired, not what the rule asked for. review_policymeans rules you set to influence the review decision moved this verification into review. No number, weight or threshold is published.check_needs_reviewappears once, however many checks need a reviewer. The checks themselves are incheck_results.processing_errormeans the verification was parked after a processing error. It is reported only while the verification isawaiting_review, and only for its current park. A verification that runs again after the error and is sent to review for another reason reports that reason instead. One that ran again and was sent back to review before this behavior was introduced still reportsprocessing_erroruntil it runs again or is decided. An item with this cause is in the Awaiting review filter of the console and not in its default Needs review filter. A verification parked by an error the platform cannot describe gets its other causes, orreview_required, instead.- After a decision,
reviewstays when checks, rules or signals sent the verification to review. A verification parked only by a processing error has noreviewonce it is decided. - A read scoped to one of your sub-accounts (
tenant_ref, for example through the AI connector) reportsshared_details_across_verificationsonly when enough of the matching verifications belong to that sub-account, and never reportslocation_change_between_verifications, because the connected verification it compares against can belong to another sub-account. When a cause left out this way was the only one, the read reportsreview_required. A read for your whole organization, with notenant_ref, reports either cause whenever it applies.device_or_connection_signalhas no such limit for a sub-account read. It can come from the same device or connection on an earlier verification under another of your sub-accounts.
A cause’s message follows the verification’s status. While the status is awaiting_review, check_needs_review and review_required read in the present tense. In every other status they read in the past tense: a decision was recorded, the verification ended, or a reviewer sent it back to the applicant for a new capture. Every other cause has one sentence. So key on code, never on the sentence: a sentence you stored at the park can differ from the one a later read returns.
| Code | Message |
|---|---|
processing_error |
This verification ran into a problem while processing, so it was sent to review instead of completing automatically. |
check_needs_review |
While waiting for review: One or more checks need a reviewer to confirm. In every other status: One or more checks needed a reviewer to confirm. |
shared_details_across_verifications |
The same applicant details appear on more than one verification. These verifications can belong to one person, or to different people. |
location_change_between_verifications |
A connected verification reports a different country. The time between the two verifications is short for the distance. |
connection_country_differs |
The country of the internet connection is not the country the applicant gave. |
device_or_connection_signal |
These facts describe the device and the internet connection that the applicant used. People share a device, and some apps hide the location of a connection. |
network_reputation_signal |
These facts describe the internet connection that the applicant used. Many people can share one office network or one phone network. |
rule_triggered |
Your rule "{name}" sent this verification to review. with the rule’s name in place of {name}. When the rule’s name cannot be found: One of your rules sent this verification to review. |
review_policy |
Rules you set to influence the review decision sent this verification to review. |
review_required |
While waiting for review: This verification needs a reviewer’s decision. In every other status: This verification needed a reviewer’s decision. |
When you meet a code you do not recognize
Section titled “When you meet a code you do not recognize”Both code lists can grow at any time, without a new API version, so do not treat either one as a closed set.
- An unknown reason code: fall back on its
category, which is a closed list of three values. - An unknown cause code: treat it as
review_required.
A client that switches on code with no default branch breaks the day a new code ships. Keep a default branch, and log the code you did not recognize.
On the webhook
Section titled “On the webhook”The kyc.session.manual_review_requested event carries the same codes, without the sentences. Its review object has two arrays:
causes: each cause code once.rule_triggeredappears once however many rules fired, because the webhook carries no rule name.checks: each check that has reasons, with itstype, itsoutcomeand its reason codes, in the same order ascheck_results.
The webhook carries no message, no rule_name, no count and no category. It is a snapshot taken when the verification was parked, so fetch the verification for its current state. See Create a webhook endpoint.
Who these are for
Section titled “Who these are for”Reasons and causes are operator data. They reach you through three channels that you authenticate: your API key, your signed webhook, and an AI assistant you connect to your account, whose get_session tool returns the session resource. They are never sent to the applicant’s browser, and the Web SDK never surfaces them.
Do not show them to the person being verified, and do not give them to that person as the reason for a decision. A cause explains why a person on your team was asked to look. It is not a reason to give the applicant. This matters most for the signal causes, from shared_details_across_verifications to network_reputation_signal in the table above. Each one can have an ordinary explanation, as its own message says, so it is context for a reviewer and never grounds for a decision on its own.
A watchlist match is published only as record_match_needs_confirmation with a count. No list, authority, name or match detail is published.
Related pages
Section titled “Related pages”- Verdict and webhooks for
outcome,decision_sourceand the poll fallback. - Track verification status for the review queue over the API.
- Create a webhook endpoint for the park event.
- Manually review a verification for what your reviewers see.