code you can switch on programmatically. The shape is:
request_id — it lets Qustody operators trace the full request path.
Code ranges
| Range | Family |
|---|---|
1000–1099 | Authentication and authorization |
1100–1199 | Validation |
1200–1299 | Policy and approval |
1300–1399 | Transaction lifecycle |
1400–1499 | Vault and resource ownership |
1500–1599 | Upstream node and chain |
1600–1699 | Server / infrastructure |
Authentication and authorization (1000–1099)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1000 | UNAUTHORIZED | 401 | Missing or invalid bearer token |
1001 | FORBIDDEN | 403 | Authenticated but RBAC permission missing for the resource+action |
1002 | API_KEY_REVOKED | 401 | The API key has been revoked or rotated |
1003 | INVALID_SIGNATURE | 401 | Webhook or request signature did not verify |
GET /v1/users/{id}/roles, or rotate the API key with POST /v1/credentials/rotate.
Validation (1100–1199)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1100 | VALIDATION | 400 | Generic validation failure (see message) |
1101 | MISSING_FIELD | 400 | A required field is absent |
1102 | INVALID_ADDRESS | 400 | Address is not a valid Quantum Chain address |
1103 | INVALID_AMOUNT | 400 | Amount is non-numeric, negative, or zero where not allowed |
1104 | DUPLICATE_EXTERNAL_ID | 409 | An object with this external_id already exists for the tenant |
1105 | OWNERSHIP_PROOF | 400 | The ownership-proof signature did not verify against the registered public key |
1106 | INVALID_EMAIL | 400 | Email address is malformed |
1107 | INVALID_URL | 400 | URL is malformed or non-HTTPS in production |
1108 | INVALID_PAGINATION | 400 | limit is out of range or cursor is malformed |
1109 | AMOUNT_OVERFLOW | 400 | Amount exceeds 256-bit unsigned bounds |
1110 | STRING_TOO_LONG | 400 | A string field exceeds its allowed length |
Policy and approval (1200–1299)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1200 | POLICY_DENIED | 422 | A policy rule rejected the transaction outright (e.g. amount > MAX_AMOUNT, address on BLACKLIST_ADDRESS) |
1201 | APPROVAL_REQUIRED | 422 | The transaction requires approvers; this is informational. The transaction is created in PENDING_AUTHORIZATION |
1202 | APPROVAL_NOT_FOUND | 404 | The approval record does not exist or has expired |
1200, modify the request or update the policy. For 1201, route the transaction to the appropriate approvers.
Transaction lifecycle (1300–1399)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1300 | TX_NOT_FOUND | 404 | The transaction ID does not exist for this tenant |
1301 | TX_INVALID_STATE | 409 | Operation not allowed in the transaction’s current state (e.g. signing a BROADCASTING transaction) |
1302 | TX_SIGNATURE_MISMATCH | 422 | The submitted post-quantum signature did not verify against the wallet’s registered public key |
1303 | TX_BROADCAST_FAILED | 502 | The Quantum Chain node rejected the raw transaction |
1304 | TX_NONCE_TOO_LOW | 422 | The nonce was already used on chain |
1301, fetch the latest state with GET /v1/transactions/{id}. For 1302, confirm the signer is using the correct key. For 1303 and 1304, cancel the transaction and resubmit.
Vault and resource (1400–1499)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1400 | VAULT_NOT_FOUND | 404 | Vault does not exist for this tenant |
1401 | VAULT_DUPLICATE | 409 | A vault with this name already exists |
Upstream node and chain (1500–1599)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1500 | NODE_UNAVAILABLE | 503 | The Quantum Chain RPC node is unreachable; circuit breaker may be open |
1501 | CHAIN_ERROR | 502 | The node returned an error (gas estimation, QVM revert, etc.) |
CUSTODY_NODE_BACKUP_RPC_URLS is configured, Qustody fails over automatically.
Server / infrastructure (1600–1699)
| Code | Type | HTTP | Meaning |
|---|---|---|---|
1600 | INTERNAL_ERROR | 500 | Unexpected server error; see request_id |
1601 | IDEMPOTENCY_CONFLICT | 422 | Same Idempotency-Key reused with a different method or path |
1602 | RATE_LIMITED | 429 | Tenant or API key exceeded the rate limit; honor Retry-After |
1600, retry once after a short delay; if it persists, contact support with the request_id. For 1601, generate a fresh idempotency key. For 1602, respect the Retry-After header and consider a Redis-backed limiter for cluster deployments.
Retry policy
| HTTP class | Retry? | Strategy |
|---|---|---|
4xx (validation, auth, conflict) | No | Fix input; do not retry |
409 TX_INVALID_STATE | No | Refresh state, then act |
429 | Yes | Back off Retry-After seconds |
502 / 503 | Yes | Exponential backoff with jitter, max 5 retries |
500 | Yes | Once after 1–2s; if persistent, escalate |
Idempotency-Key on retried mutating requests so the server collapses duplicates.
Webhook delivery errors
Webhook delivery failures don’t surface to the API caller. They appear in:GET /v1/webhooks/{id}/deliveries— per-delivery status, last response code, retry count.webhook.delivery.failedevents — when an endpoint exhausts its retry budget.
410 Gone, Qustody marks it DISABLED permanently. Recreate it to resume deliveries.