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

# Errors

> Error codes, the shapes of error responses, and how to handle each on your side.

An error has an HTTP status and, in most cases, a machine-readable `code`. The body comes in one of four shapes, described in the [API reference](/api-reference/overview#errors); read `error.code`, then `code`, then fall back to the status. The [server SDKs](/integration/sdks) do this for you and raise one exception type.

These are request errors. Why a verification was declined is not an error: it is the verification's `reason`, listed in [Decision reasons](/core-technology/decision-reasons).

## Authentication

| HTTP | Code | What to do |
| - | - | - |
| 401 | `MISSING_API_KEY` | Send `X-API-Key`. |
| 401 | `INVALID_API_KEY` | Check the key; test and live keys differ. |
| 401 | `NO_SECRET_KEYS` | Create a secret key for the workspace in the console. |
| 401 | `MISSING_SIGNATURE` | Sign the request. |
| 401 | `INVALID_SIGNATURE` | See [when the signature does not match](/getting-started/api-authentication#when-the-signature-does-not-match). |
| 403 | `WORKSPACE_SUSPENDED` | The workspace is suspended; reactivate it in the console. |
| 403 | `TENANT_ARCHIVED` | The account is closed. Contact support. |

## Creating a verification

| HTTP | Code | What to do |
| - | - | - |
| 402 | `PAYMENT_METHOD_REQUIRED` | The free verifications are used up (or a deadline set on your account has passed). Add a payment method in the console to create live verifications. The body also has `free_verifications_remaining`, `trial_ends_at` and `trial_active`. |
| 422 | — | A field is invalid; `errors` names it. |

## Consent, upload and submit

These come up only if you [capture in your own UI](/integration/web/custom-ui).

| HTTP | Code | What to do |
| - | - | - |
| 403 | `CONSENT_REQUIRED` | Record the person's consent before uploading. |
| 402 | `PAYMENT_METHOD_REQUIRED` | On submit: the account had no way to pay any more (the payment method was removed, or a deadline set on your account passed) after the verification was created. Running out of free verifications alone does not cause this: verifications created before they ran out still complete. Add a payment method, then submit again. |
| 422 | `MAX_ATTEMPTS_REACHED` | A facial age estimation that moved to a document has no attempts left for a new selfie. Create a new verification. |
| 422 | `INVALID_STATUS` | The verification is not in a state that can be submitted, or belongs to another workspace. |
| 422 | `MISSING_REQUIRED_MEDIA` | Upload the missing images, then submit. |
| 422 | `VALIDATION_ERROR` | A field is invalid. |
| 422 | — | A field is invalid, or a document image arrived after every required image was already in: `errors` names it. |
| 403 | — | `{"message": "Access denied to this verification."}` on consent: the verification belongs to another workspace. |

An upload is checked as soon as it arrives. If the image is not usable, the upload fails with `422` and a code the person can act on; ask them to retake it:

| Code | Ask the person to |
| - | - |
| `FACE_NOT_FOUND` | Show their face in the frame. |
| `FACE_INVALID_SIZE` | Move so the face fills the frame, not too close or too far. |
| `FACE_TOO_BLURRY` | Hold still, in focus. |
| `FACE_BRIGHTNESS_INVALID` | Find even light, neither too dark nor too bright. |
| `FACE_OCCLUDED` | Remove anything covering the face. |
| `FACE_ALIGNMENT_FAILED`, `FACE_TOO_TILTED` | Look straight at the camera. |
| `FACE_EYES_CLOSED` | Keep their eyes open. |
| `FACE_QUALITY_UNSUITABLE`, `FACE_VALIDATION_FAILED` | Retake the selfie. |
| `DOCUMENT_NOT_FOUND`, `DOCUMENT_OUTLINE_NOT_FOUND` | Show the whole document on a plain surface. |
| `DOCUMENT_TOO_FAR` | Bring the document closer. |
| `DOCUMENT_PORTRAIT_NOT_FOUND` | Show the side of the document with their photo (a closed passport cover or the back of a card has none), fully in the frame, in good light and without glare. |
| `DOCUMENT_TOO_BLURRY` | Hold the document still, in focus. |
| `DOCUMENT_BRIGHTNESS_INVALID` | Avoid glare and shadow. |
| `DOCUMENT_NOT_READABLE`, `DOCUMENT_VALIDATION_FAILED` | Retake the document so the text is readable. |

`DOCUMENT_OUTLINE_NOT_FOUND`, `DOCUMENT_TOO_FAR` and `DOCUMENT_PORTRAIT_NOT_FOUND` are not sent for every workspace; handle them anyway.

## Reading results and blocking

| HTTP | Code | What to do |
| - | - | - |
| 404 | — | `{"message": "Resource not found"}`: the verification or image does not exist. |
| 403 | — | `{"message": "Access denied to this verification."}`: reading a verification, its document, its estimation or its media with the key of another workspace. |
| 404 | `MEDIA_NOT_FOUND` | The image was deleted by retention or erasure. |
| 403 | `FACE_BLOCKLIST_DISABLED` | The face blocklist is not enabled for your account. |
| 403 | `VERIFICATION_MISMATCH` | Blocking a face: the verification belongs to another workspace. |
| 422 | `VALIDATION_ERROR` | Blocking a face: `reason_code` is unknown, or there is nothing to block; see [Face blocklist](/core-technology/face-blocklist). This body has both `error` and `errors`. |

## Test outcomes

| HTTP | Code | What to do |
| - | - | - |
| 403 | `TEST_WORKSPACE_ONLY` | Setting a test outcome works in a [test workspace](/integration/sandbox-testing#set-the-outcome-from-your-code) only. |
| 422 | `INVALID_STATUS` | The verification is already final (`approved`, `declined`, `abandoned`, `expired`), or already in `review` when you set `review`. Create a new verification, or set another outcome on one in review. |

## Webhook subscriptions

| HTTP | Code | What to do |
| - | - | - |
| 422 | `WEBHOOK_SUBSCRIPTION_LIMIT` | The workspace already has 50 [webhook subscriptions](/integration/webhooks#webhook-subscriptions). Delete one you no longer use. |
| 422 | — | The `url` breaks [the webhook URL rules](/integration/webhooks#the-webhook-url), or `statuses` holds a status that is not a decision: `errors` names the field. |
| 404 | — | `{"message": "Resource not found"}`: deleting a subscription that does not exist in this workspace. |

## Any endpoint

| HTTP | Code | What to do |
| - | - | - |
| 429 | `RATE_LIMIT` | Wait `Retry-After` seconds; see [Rate limiting](/integration/rate-limiting). |
| 5xx | — | Retry a `GET` with backoff. See below for a `POST`. |

## When a request times out

There is no idempotency key.

* **Create:** create again. An unused verification is never started, expires after seven days and is not billed: a verification is billed when it is submitted.
* **Consent:** send it again; the same values answer `200`.
* **Upload:** upload again; the new image replaces the previous one of the same type and side.
* **Submit:** send it again. If the first one went through, the second answers `422 INVALID_STATUS`: read the verification to confirm it is `submitted`.

## Handling errors well

* Log the HTTP status, the `code` and the `message`.
* Show the person a message for the upload codes above; show a generic message for everything else and fix the cause on your side.
* Do not retry `4xx` errors other than `429` unchanged: they fail again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.