> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pruva.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling failed checks

> Tell apart a no-match, a failure, and an error, and respond to each correctly.

Not every check returns a clean match, and how you treat the other outcomes decides whether your integration is robust or brittle. The key is to separate three things that are easy to lump together: a **no-match**, a **failure**, and an **error**.

## The three cases

| Case     | How it looks                            | What it means                              | Charged? |
| -------- | --------------------------------------- | ------------------------------------------ | -------- |
| No match | `200`, `"status": "not_found"`          | The lookup ran; the record does not exist. | Yes      |
| Failure  | `200`, `"status": "failed"`, or a `502` | The check could not be completed.          | No       |
| Error    | `4xx` error envelope                    | The request was rejected before running.   | No       |

<Warning>
  `not_found` is a successful check with a real answer, and it is **charged**. Do not treat it as an error or retry it. Retrying a `not_found` just runs and bills the same lookup again with the same result.
</Warning>

## Responding to each

<Steps>
  <Step title="found">
    Proceed. Read the `result` object by key for the details you need.
  </Step>

  <Step title="not_found">
    A definitive "no record." Handle it in your product, ask the user to re-check the number, offer another method, and move on. Do not retry.
  </Step>

  <Step title="failed">
    The provider could not answer, usually a timeout or outage. This is safe to retry **once**, after a short delay. Do not loop.
  </Step>

  <Step title="4xx error">
    Fix the cause before retrying, since the same request will fail the same way. See below.
  </Step>
</Steps>

## Reading why a check failed

A `failed` verification carries an `errorMessage` describing why, in plain language: liveness not confirmed, no face detected, the selfie did not match, the document could not be authenticated, and so on. Surface this to your own support view; it is usually enough to tell a genuine user problem from a transient one.

## Matching errors to causes

| Code  | Cause                                                     | Fix                                                             |
| ----- | --------------------------------------------------------- | --------------------------------------------------------------- |
| `402` | Wallet too low                                            | Top up. The error's `details` shows the price and your balance. |
| `403` | Scope not active, or key not allowed, or org not approved | Activate the scope, widen the key, or complete approval.        |
| `422` | Missing or invalid inputs                                 | Correct the request body.                                       |
| `502` | Provider could not complete                               | Retry once; if it persists, the provider may be down.           |

## A resilient pattern

<Steps>
  <Step title="Persist the reference immediately">
    Store the `reference` from the response before anything else. It is your record that the check happened and what it cost.
  </Step>

  <Step title="Branch on status, not on presence of result">
    Switch on `status`: `found`, `not_found`, `failed`. Do not infer failure from an empty `result`; a `not_found` legitimately has an empty result.
  </Step>

  <Step title="Retry only failures, only once">
    Retry `failed` and `502` a single time. Never retry `not_found`, `402`, `403`, or `422`, since they will not change without action on your side.
  </Step>

  <Step title="Reconcile from the list">
    If you are unsure whether a check ran, [list verifications](/api-reference/list-verifications) rather than re-running, so you do not pay for a duplicate. See [References and retrieval](/concepts/references).
  </Step>
</Steps>
