Skip to content

Review reasons

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.

  • Per-check reasons. check_results[].reasons on GET /v1/sessions/{id} says why one check needs a person, was only partly performed, or could not produce a result. Each reason is a code, a category and a message, with a count when the reason counts items.
  • Session-level causes. review.causes on GET /v1/sessions/{id}, and on each item of GET /v1/sessions, says why the whole verification was sent to a reviewer. Each cause is a code and a message, with a rule_name when 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."
}
]
}
]
}
}

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.

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_judgment or did_not_finish. When nothing more specific is known, that reason is check_needs_confirmation for a check at review, or check_did_not_finish for a check at fail or error.
  • 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 reasons and 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 reasons to 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 is review, fail or error.
  • An ID verification code takes its category from what happened to the part of the check it describes: needs_judgment when that part was sent for review, not_checked when that part was skipped. Each code is recorded one way, except document_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 always needs_judgment.
  • selfie_match_not_run is expected on a flagged ID check when the workflow has no selfie step. It is not a failure. Like every not_checked reason, among them second_side_not_read, images_not_analyzed, name_check_not_run and name_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_confirmation carries a count: 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.

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.

  • causes is never empty. When nothing more specific can be said, it holds review_required.
  • The order is stable. processing_error comes first, then check_needs_review, then the signal causes in the order of the table below, then rule_triggered, then review_policy. review_required appears only when no other cause does.
  • rule_triggered appears once for each distinct rule name, with that name in rule_name. All the rules whose name cannot be found produce one entry with no rule_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_policy means rules you set to influence the review decision moved this verification into review. No number, weight or threshold is published.
  • check_needs_review appears once, however many checks need a reviewer. The checks themselves are in check_results.
  • processing_error means the verification was parked after a processing error. It is reported only while the verification is awaiting_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 reports processing_error until 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, or review_required, instead.
  • After a decision, review stays when checks, rules or signals sent the verification to review. A verification parked only by a processing error has no review once it is decided.
  • A read scoped to one of your sub-accounts (tenant_ref, for example through the AI connector) reports shared_details_across_verifications only when enough of the matching verifications belong to that sub-account, and never reports location_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 reports review_required. A read for your whole organization, with no tenant_ref, reports either cause whenever it applies. device_or_connection_signal has 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.

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.

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_triggered appears once however many rules fired, because the webhook carries no rule name.
  • checks: each check that has reasons, with its type, its outcome and its reason codes, in the same order as check_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.

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.