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.
The ticket ID
Section titled “The ticket ID”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.
Status codes
Section titled “Status codes”| 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 limits
Section titled “Rate limits”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.
Two commonly misread codes
Section titled “Two commonly misread codes”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