Skip to content

Errors

Every error response has the same shape, whatever produced it:

{
"message": "Route GET:/v1/does-not-exist not found",
"error": "Not Found",
"statusCode": 404,
"ticketId": "fd7c6076-29ac-4b25-94e0-6b74be6bb955"
}
Field Use it for
message A human-readable description. Do not parse it; the wording is subject to change.
error The status text. Redundant with statusCode, and present for readability.
statusCode The HTTP status, repeated in the body so that a logged payload is self-contained.
ticketId A unique identifier for this specific failure. See below.

Branch on statusCode, never on message.

Every error carries a ticketId. The same identifier is recorded server-side together with the full context of the failure.

Include it in any support request. A request quoting a ticket ID can be looked up directly; one without requires the failure to be reconstructed from a description and a timestamp.

When logging error responses, log the complete envelope rather than the message alone. The ticket ID is the field of lasting value.

Code Means Retry?
400 The request body or query failed validation. No; correct the request.
401 Missing, malformed or expired credentials. After refreshing the token.
403 Authenticated but not permitted: an insufficient role, or a scope not granted. No.
404 No such route, or no such record visible to the caller. No.
409 Conflict with the current state. Only after re-reading the resource.
429 Rate limited. Rate limit headers are returned on every response: x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset. Yes, after x-ratelimit-reset.
5xx A server-side failure. Yes, with backoff. Retain the ticket ID.

Rate limit headers are on every response, not just a 429:

Header
x-ratelimit-limit The budget for this route
x-ratelimit-remaining What is left of it
x-ratelimit-reset When it refills

The budget is per route, not per account. Most routes carry their own allowance, commonly ten, twenty or thirty requests per minute. A few are substantially tighter where abuse is a concern: sign-in is ten per minute, AI endpoints ten per minute, and account deletion twice per day.

Read x-ratelimit-limit from the response to the request being made rather than assuming a global figure. The limit on one endpoint does not indicate the limit on another.

On 429, wait until x-ratelimit-reset before retrying. Retrying within a closed window consumes the subsequent one.

404 does not always indicate non-existence. Where a response of “exists but is not accessible” would itself disclose information, the API returns 404 rather than 403. An inaccessible record and a non-existent one are indistinguishable externally. A 404 is therefore not evidence that an identifier is available for reuse.

403 concerns authorisation, not authentication. Where a 403 is returned on a route expected to be accessible, check the scopes granted to the token before checking the member’s role: a missing scope and a missing role produce the same status. See the MCP scope table.

Something wrong on this page? Report it