Errors
The one flat error envelope every endpoint returns, the status codes it is paired with, and which refusals are worth retrying.
Every error response has the same flat shape:
{
"error": "VALIDATION_ERROR",
"message": "name is required",
"details": [{ "field": "name", "issue": "required" }]
}
error is a stable machine-readable code, message is human-readable, and
details is optional. The envelope is never nested and never carries a
success flag — branch on the HTTP status and on error, never on the presence
of a payload.
Status codes
| status | meaning |
|---|---|
| 400 | the request is malformed or a field is missing |
| 401 | not authenticated — missing, malformed, expired or revoked token |
| 403 | authenticated, but not permitted; on the public API this is almost always INSUFFICIENT_SCOPE |
| 404 | the resource does not exist |
| 409 | a conflict — a concurrent update was lost, or the resource is already in the requested state |
| 422 | the request is well-formed but cannot be processed |
| 429 | the token’s rate limit was exceeded |
| 500 | an unexpected failure on our side |
| 503 | the service is still starting — retryable |
INSUFFICIENT_SCOPE is the refusal to design for
Because the public API is default-deny, the most common error a working
integration meets is not a validation failure — it is a scope refusal. A token
reaches only the entities and actions it was issued for, and everything else
answers 403 INSUFFICIENT_SCOPE with the entity and action named in the
message. See Authentication for the scope model and
the exact body.
Treat it as a configuration error, not a transient one: it is deterministic for a given token and endpoint, and it clears only when the token is re-issued with the missing scope.
What is worth retrying
- 429 and 503 are transient. Back off and retry.
- 5xx other than 503 may be transient; retry with backoff and a ceiling.
- 400, 401, 403, 404, 409, 422 are not. The same request will fail the same
way.
403 INSUFFICIENT_SCOPEin particular is a token that needs re-issuing, and retrying it only spends your rate limit.
Code lists in the reference are samples, not closed unions
Individual endpoints in the reference document the error codes that endpoint’s
service actually emits. Those lists are a sample of what you are likely to
see, not an exhaustive union — a client must handle an unrecognised error
value by falling back to the HTTP status.
See also
- Rate limits —
429andRetry-Afterin detail - Webhooks — the inbound receiver’s own status codes, which follow the same envelope
- Versioning and deprecation — why new
errorcodes can appear without notice