Errors
Every error the API returns carries one envelope, whatever the route:
{
"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
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.
unauthorized (HTTP 401)
The deployment requires Authorization: Bearer <api key> on /v1 routes and the header is missing or wrong.
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.
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}.
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.
not_found (HTTP 404)
No such resource: an unknown id, or a route that does not exist.
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.
rate_limited (HTTP 429)
Too many requests in the current window. Back off and retry; idempotency keys make retries safe on create and fund.
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.
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.
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 |