Guide
Errors and rate limits
Error format, status codes and request limits.
Response envelope
Resource endpoints wrap every response in the same envelope:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable explanation",
"field": "X-Tenant-ID",
"retryable": false
},
"meta": {
"requestId": "...",
"correlationId": "...",
"timestamp": "2026-11-02T10:00:00Z",
"version": "1.0"
}
}
On success success is true and the result is in data. Fields without a value are left out. Include
meta.requestId when you contact support.
The token endpoints (/v1/auth/...) return plain OAuth-style JSON instead of the envelope.
Always branch on the HTTP status first and use error.code for detail. Some statuses currently carry a
generic code — for example a missing or invalid bearer token returns 401 with error.code
VALIDATION_ERROR. Treat any 401 as "get a new token".
Error codes
| Code | Typical status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400, 422 | A header or field is missing or invalid, or the body doesn't match the schema. Check field. Also returned with 401 for a missing or invalid token. |
UNAUTHORIZED | 401 | Token rejected. Some 401s carry VALIDATION_ERROR instead — branch on the status. |
FORBIDDEN | 403 | Missing scope, or a tenant that isn't yours. |
NOT_FOUND | 404 | The resource or tenant doesn't exist. |
CONFLICT | 409 | The request conflicts with the resource's current state. |
IDEMPOTENCY_CONFLICT | 409 | Idempotency key reused with a different body, or still in flight. |
RATE_LIMITED | 429 | Too many requests — see below. |
REPLAY_EXPIRED | 403 | A webhook delivery is too old to replay. |
SERVICE_UNAVAILABLE | 503 | Temporary problem on our side. Retry with backoff. |
INTERNAL_ERROR | 500 | Unexpected error. Retry with backoff; contact us if it persists. |
Some endpoints return more specific codes; the API reference lists them.
Retrying
Retry when retryable is true, on 429, and on 5xx. Use exponential backoff with jitter and keep the
same X-Idempotency-Key so a retry never creates a duplicate.
Rate limits
Limits apply per tenant, separately for reads and writes per hour, plus a per-second burst limit. Your limits depend on your agreement; ask us if you need them raised.
Rate-limited responses tell you where you stand (token and health endpoints don't carry these headers):
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window |
X-RateLimit-Remaining | Requests left in the window |
X-RateLimit-Reset | Seconds until the window resets |
The headers can occasionally be missing; treat them as advisory and rely on 429 + Retry-After.
When you exceed a limit you get 429 RATE_LIMITED with a Retry-After header (seconds). Wait that long
before retrying.