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

# Errors

> Status codes the suite returns, which are worth retrying, and the successful responses that still say no.

Every failure returns the same body, whichever endpoint was called. It carries the same envelope
fields as a successful response, with `message` in place of `data`:

```json theme={null}
{
  "code": 409,
  "timestamp": 1751975600361,
  "message": "Consent for this session is not complete yet",
  "transaction_id": "de90173f-8255-4ac0-b26b-a41b8a872fee"
}
```

Branch on `code`, not on the message text. The wording may be reworded; the status will not.

`transaction_id` is present when the call reached the source, so a failed call gives you the same
reference to quote as a successful one. It is `null` when the request never got that far, such as a
rejected key or a request that failed validation.

| Status | When                                                                                                    |
| ------ | ------------------------------------------------------------------------------------------------------- |
| `400`  | The source rejected the request as malformed.                                                           |
| `401`  | The `Authorization` header is missing or malformed, or the API key was rejected.                        |
| `403`  | The API key is valid but is not permitted to call this endpoint, or is not linked to an active account. |
| `404`  | No session with that id, or the source holds no data for what was asked.                                |
| `409`  | The DigiLocker session has not finished collecting consent. Poll its status and retry.                  |
| `422`  | The request failed validation, ours or the source's. `message` names the fields that were wrong.        |
| `429`  | Out of credits, or too many requests.                                                                   |
| `502`  | The source could not be reached, or answered in a way we could not read.                                |
| `503`  | The source is temporarily unavailable.                                                                  |
| `504`  | The source did not answer in time.                                                                      |
| `500`  | Something failed on our side.                                                                           |

## Retrying safely

* `429`, `503` and `504` are transient. Retry with exponential backoff.
* `502` indicates a source-side failure rather than a problem with your request. A retry may work; do
  not retry in a tight loop.
* Any other `4xx` will not change on retry. Fix the request, or wait for the state to change in the
  case of `409`.

<Warning>
  A penny drop moves one rupee into the account being verified. Retrying it repeats that transfer and
  is charged again. Retry it only when you have received no response at all, and never automatically
  on a `4xx`.
</Warning>

## A 200 is not always a yes

Bank verification returns `200` even when the account could not be checked: the transport worked, the
verification did not. In those responses `message` carries the reason and the fields below it are
absent:

```json theme={null}
{
  "code": 200,
  "timestamp": 1751975600361,
  "transaction_id": "a3083c7e-a879-402c-a8db-506d14f9f694",
  "data": { "message": "NPCI Unavailable" }
}
```

Read `message` before anything else, and treat a missing `account_exists` as "not answered" rather
than as "no". Reasons include an offline beneficiary bank, an unavailable NPCI, a declining source
bank, and an account the method cannot reach, such as an NRE account.

PAN behaves the same way: a `status` other than `valid` with a populated `remarks`, such as `Holder
is Deceased`, arrives on a successful call.
