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 published contract
{
"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 |
|---|---|---|
| 409 | conflict | The request conflicts with the document's current state; details.allowed names the legal transitions. |
| 409 | idempotency_conflict | This Idempotency-Key was already used for a different request. |
| 403 | sandbox_isolation | A test key on a live organization, or a live key on a sandbox. |
| 429 | sandbox_quota | The sandbox document cap — reset the sandbox to clear it. |
| 400 | invalid_request | A parameter is malformed or out of range. |
| 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. |
| 403 | capability_unavailable | The key has the scope, but your organization doesn't have the capability behind it. |
| 404 | not_found | No such resource for your organization. |
| 429 | rate_limited | Per-key limit exhausted. 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 request is logged on our side. |
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.