import { Aside } from '@astrojs/starlight/components';

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`.

<Aside type="caution" title="These are for your reviewers">
  Reasons and causes explain why a person on your team was asked to look at a verification. Do not
  show them to the person being verified, and do not give them to that person as the reason for a
  decision. See [Who these are for](#who-these-are-for).
</Aside>

## Two levels

- **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.

```json
{
  "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

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

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.

{/* review-reason-codes:start */}

| 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.                                                                                                                                          |

{/* review-reason-codes:end */}

`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

`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.

{/* review-cause-codes:start */}

| 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.                                             |

{/* review-cause-codes:end */}

## 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

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](/developers/webhooks#the-event-payload).

## 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

- [Verdict and webhooks](/developers/sdks/verdict-and-webhooks) for `outcome`, `decision_source` and the poll fallback.
- [Track verification status](/guides/verification/track-status) for the review queue over the API.
- [Create a webhook endpoint](/developers/webhooks) for the park event.
- [Manually review a verification](/guides/verification/reviewer-manual-review) for what your reviewers see.