Core concepts
Errors
The error envelope, the codes you will meet, and how to handle each one.
Every failure returns the same envelope. This page is a placeholder for the error contract.
The envelope#
{
"error": {
"code": "rate_limited",
"message": "Too many requests",
"requestId": "req_01H..."
}
}Codes#
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The body or query failed validation |
unauthorized | 401 | The key is missing, malformed or revoked |
forbidden | 403 | The key lacks the required scope |
not_found | 404 | The resource does not exist in this tenant |
conflict | 409 | The write conflicts with current state |
rate_limited | 429 | Too many requests; retry after the delay |
internal_error | 500 | An unexpected failure; safe to retry |
Always log the request id
The requestId is the fastest way to correlate a failure with server logs. Include it in every error report.
Retrying#
Retry a 429 or a 5xx with exponential backoff. Do not retry a 4xx other than 429: the request will fail the same way.
async function withRetry<T>(run: () => Promise<T>, attempts = 4): Promise<T> {
for (let attempt = 0; attempt < attempts; attempt += 1) {
try {
return await run();
} catch (error) {
if (attempt === attempts - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 100));
}
}
throw new Error("unreachable");
}Idempotency keys#
For a retried write, send an Idempotency-Key header. The server replays the original response instead of applying the write twice.