Every failure comes back in one shape, with an HTTP status that means what it says. There is no second error format and no stack traces — a message is safe to log and safe to show.
The envelope
From the live spec
{
"error": {
"code": "insufficient_scope",
"message": "This key does not have the treasury:read scope",
"docs_url": "https://api.rivet.network/v1/docs#errors-insufficient_scope"
}
}The three fields
code— the machine-readable reason, from a closed set. Branch on this, never on the message.message— human-readable and safe to surface. Its wording can change; treat it as text for a person, not a value for your code.docs_url— a deep link to the explanation of that exact code in the reference. It is there so a support ticket can start with the answer instead of the question.
The codes
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_request | A parameter is malformed or out of range (unknown query parameters are rejected). |
| 401 | unauthorized | Missing, unknown or revoked key. |
| 403 | forbidden | Key management from a non-admin session. |
| 403 | insufficient_scope | The key was never granted the scope this endpoint needs. |
| — | step_up_required | See the reference. |
| 403 | capability_unavailable | The key has the scope, but your organization doesn't have the capability behind it (e.g. treasury before verification). |
| — | payment_limit_exceeded | See the reference. |
| 404 | not_found | No such resource *for your organization* — including documents on connections you are not a party to. |
| — | conflict | See the reference. |
| — | idempotency_conflict | See the reference. |
| — | sandbox_isolation | See the reference. |
| — | sandbox_quota | See the reference. |
| 429 | rate_limited | Per-key limit exhausted (default 120/min, 10,000/day). Honor `Retry-After`. |
| 503 | not_enabled | The family isn't switched on in this environment yet. |
| 503 | service_unavailable | A backing service is unreachable. Retry with backoff. |
| 500 | internal_error | Our fault. The message never carries internals; the request is logged on our side. |
| — | payment_required | See the reference. |
| — | payment_amount_stale | See the reference. |
| — | approval_required | See the reference. |
| — | not_payable | See the reference. |
| — | financed_invoice_rivet_rail_only | See the reference. |
| — | x402_settle_failed | See the reference. |
| — | x402_already_settled | See the reference. |
| — | owner_not_verified | See the reference. |
Handling them
invalid_requestis yours to fix — a parameter is malformed or out of range. Retrying unchanged will fail identically.unauthorizedmeans the key is missing, unknown or revoked. Don’t retry; mint a new key.insufficient_scopeandcapability_unavailablelook alike and are not: the first means the key was never granted the scope, the second that the key has it but your organization does not have the capability behind it.not_foundon a document is often the party rule doing its job, not a bug — see the document model.rate_limited,service_unavailableandnot_enabledare the retryable family. Back off; for a 429 honorRetry-After.
New codes may be added within v1 — that is an additive change and does not break the contract. Handle an unrecognized code by its HTTP status rather than crashing on it.