# Errors

Every error the API returns carries one envelope, whatever the route:

```json
{
  "error": {
    "code": "not_found",
    "message": "escrow esc_1234 not found",
    "docsUrl": "https://quitanza.com/docs/errors.html#not_found"
  }
}
```

The `code` is stable and machine-matchable; the `message` is for humans and may change; `docsUrl` points at the section below that explains the code. Match on the code, never on the message.

## The codes

<a id="bad_request"></a>
### bad_request (HTTP 400)

The request body is malformed: missing required fields, invalid JSON, or values of the wrong shape. Fix the request; retrying unchanged will fail the same way.

<a id="unauthorized"></a>
### unauthorized (HTTP 401)

The deployment requires `Authorization: Bearer <api key>` on `/v1` routes and the header is missing or wrong.

<a id="forbidden"></a>
### forbidden (HTTP 403)

The operation is admin-only and this deployment cannot perform it, e.g. issuer rotation on an open sandbox with no API key configured.

<a id="mandate_violation"></a>
### mandate_violation (HTTP 403)

The escrow would exceed the mandate the payer acts under: per-escrow cap, cumulative cap, asset allowlist, counterparty allowlist, or expiry. The mandate's usage is inspectable at `GET /v1/mandates/{id}`.

<a id="unauthorized_ruling"></a>
### unauthorized_ruling (HTTP 403)

A dispute ruling lacked the required signatures: both parties (co-signature) or the named arbiter. Also returned for malformed split shares: shares that do not sum to 10000 basis points, are not integers, or appear on a non-split ruling. The platform never rules.

<a id="not_found"></a>
### not_found (HTTP 404)

No such resource: an unknown id, or a route that does not exist.

<a id="invalid_state"></a>
### invalid_state (HTTP 409)

The escrow cannot make that transition from its current state, e.g. delivering before funding, or disputing a settled matter. Fetch the escrow to see where it stands.

<a id="rate_limited"></a>
### rate_limited (HTTP 429)

Too many requests in the current window. Back off and retry; idempotency keys make retries safe on create and fund.

<a id="unprocessable"></a>
### unprocessable (HTTP 422)

The request was well-formed but the operation could not proceed, e.g. a dispute opened by a key that is not a party.

<a id="custody_failed"></a>
### custody_failed (HTTP 502)

Custody could not settle: the chain was unreachable or the transaction did not confirm. Nothing is lost: the settlement is journaled, the escrow carries `custody.status: "settle-failed"`, a `custody.failed` event fired, and `POST /v1/escrows/{id}/custody/retry` resumes it. A quitanza is never issued without a confirmed settle.

<a id="payment_invalid"></a>
### x402 payment errors (HTTP 402)

The x402 handshake refuses a payment with the same envelope and one of these codes: `malformed_header`, `version_mismatch`, `scheme_mismatch`, `wrong_resource`, `wrong_payee`, `wrong_asset`, `bad_signature`. Each names exactly what disagreed between the offer and the signed payment authorization.

## Retry guidance

| Code | Retry? |
|---|---|
| `bad_request`, `unprocessable`, x402 codes | No. Fix the request first |
| `unauthorized`, `forbidden` | No. Fix credentials or use a capable deployment |
| `mandate_violation`, `unauthorized_ruling` | No. Authority is missing, not luck |
| `invalid_state` | No. Re-read the escrow state |
| `not_found` | No, unless you created the resource a moment ago |
| `rate_limited` | Yes, after backing off |
| `custody_failed` | Yes, via the custody retry route |
