Errors
Every v2 error has the same shape and a stable code to branch on.
The error envelope
{
"error": {
"code": "missing_scope",
"message": "Token lacks the investors:read scope.",
"request_id": "9a9d1c5e-4f2b-4c8e-9a7d-2f1e6b3c4d5a",
"details": []
}
}
code— stable and machine-readable. Branch on this.message— one sentence a person can act on. Show it, log it, but never parse it.request_id— the same value as theX-Request-Idheader. Quote it when you contact support.details— one entry per problem when there's more to say: a field and reason for each failure onvalidation_failed, the permissions that would allow the call onmissing_permission.
Status codes
| Status | When |
|---|---|
200 / 201 | Success. 201 when something was created. |
202 | Accepted as an async job. |
204 | Success with no body, typically a delete. |
400 | The request is malformed. |
401 | No usable token. |
403 | The token is valid but not allowed to do this. |
404 | Not found — or not visible to this token. v2 never reveals that a record you can't see exists. |
409 | Conflicts with the current state, such as a name that's taken or a locked folder. |
413 | The body is over the size limit. Nothing was stored. |
422 | Validation failed, or the operation refused in a documented way. |
429 | Rate limited. |
502 / 503 | An upstream service (for example identity verification) failed or timed out. Retry after Retry-After. |
Codes to handle
Authentication and access
| Code | Status | What to do |
|---|---|---|
invalid_token | 401 | The token is missing, malformed or unknown. Check the Authorization header. |
expired | 401 | The token has passed its expiry. Create a new one. |
revoked | 401 | The token was revoked. Create a new one. |
org_disabled | 403 | API access is switched off for the organisation. An administrator can turn it on in Settings → API Suite. |
scope_disabled | 403 | The area this scope covers is switched off for the organisation. |
missing_scope | 403 | The token lacks the operation's scope — or its user no longer holds the matching permission. |
missing_permission | 403 | The acting user lacks a permission the operation needs; details lists the ones that would work. |
ip_blocked | 403 | The caller's IP isn't on the organisation's or the token's allowlist. |
user_disabled | 403 | The token's user is suspended. |
rate_limited | 429 | Wait Retry-After seconds, then retry. |
Requests
| Code | Status | Meaning |
|---|---|---|
validation_failed | 422 | One details entry per problem, each naming the field. |
invalid_cursor | 422 | The cursor is malformed or doesn't match the request's filters and sort. |
not_found | 404 | No such record, or not one this token can see. |
method_not_allowed | 405 | The path exists but not with this method. |
payload_too_large | 413 | The body is over the limit. |
Operations document their own domain codes too — for example folder_locked, form_not_completable or subscription_disabled — on their reference pages.
Rate limits
Each token has a per-minute budget — 60 by default — and every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Over it you get 429 rate_limited with Retry-After. See Rate limits for how budgets are set and counted, the operations with tighter limits, and retry code.
Retrying safely
- Retry
429,502,503and network timeouts, with exponential backoff and jitter. - Don't retry other
4xxresponses unchanged — fix the request first. - Make writes retry-safe with an
Idempotency-Key, so a retried create never makes two records.
Request IDs
Every response — success or failure — carries an X-Request-Id. Log it alongside your own request. Every call, including rejected ones, is recorded in your organisation's API audit log under that id, so support can find exactly what happened.