Errors & Rate Limits
This page covers the REST API. MCP reports errors differently — inside a successful JSON-RPC response — and has its own page: MCP errors & retries.
The response envelope
Every REST response has the same shape. On success error is null; on failure data is null:
{ "data": { "…": "…" }, "error": null, "meta": { "limit": 25, "offset": 0, "total": 3 } }
{ "data": null, "error": { "id": "a1b2c3d4", "code": "INSUFFICIENT_SCOPE", "message": "this API token does not have the required scope: sbom:write" } }
error.codeis stable and machine-readable. Branch on it, never onmessage.error.messageis a human-readable explanation; it may change.error.args, when present, carries details — the invalid parameter, the allowed values, the plan a feature needs, orretryAfterSeconds.error.id, when present, is a correlation ID. Quote it to support.
A request to a path that does not exist at all gets a plain-text 404 from the web server, not an envelope.
Error codes
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_FAILED |
A JSON body or query parameter failed validation. Bodies reject unknown fields; error.args names the parameter |
Fix the request |
| 400 | SCOPE_INVALID |
A scope at token creation is unknown, a wildcard or a preset name. The message names it | Send explicit scope names from GET /api/v1/tokens/scopes |
| 400 | INVALID_REQUEST |
A malformed multipart form | Fix the request |
| 400 | MISSING_FILE, UNSUPPORTED_FORMAT, PARSE_ERROR |
SBOM upload problems — see Upload an SBOM | Fix the file |
| 400 | INVALID_STATUS, INVALID_SEVERITY |
An unknown findings filter value | Use a documented value |
| 401 | UNAUTHORIZED |
No Authorization header |
Send Authorization: Bearer aci_… |
| 401 | INVALID_TOKEN |
The token is malformed, unknown, expired or revoked | Create a new token |
| 402 | FEATURE_GATED |
Your plan does not include the feature. error.args.minTierName names the lowest plan that does |
Upgrade, or stop calling it |
| 403 | FORBIDDEN |
The path does not accept API tokens, or the API is not enabled for your deployment | Use a signed-in session or MCP; scopes will not help |
| 403 | OWNER_REQUIRED |
The scope you tried to grant can be granted only by an Owner | Ask an Owner to create the token |
| 403 | INSUFFICIENT_ROLE |
Your role may not grant one of the requested scopes | Ask someone with a higher role |
| 403 | SUPPORT_SESSION_NO_MINT |
A token was created or revoked from a support session | Do it from your own signed-in session |
| 403 | ORG_SUSPENDED |
The organisation is suspended. Returned to a token on every token-accessible surface (including the MCP endpoint); the response carries no plan or organisation details, and the suspension can take up to 30 seconds to take effect | Contact support; calls work again once the organisation is reactivated |
| 403 | PII_UNAVAILABLE |
A response containing names or emails is not available for this token or deployment | Use a token that holds the identity scope, or contact support |
| 403 | INSUFFICIENT_SCOPE |
The token lacks the scope named in the message — or holds pii:read that cannot be honoured |
Create a token with the right scopes |
| 403 | HUMAN_APPROVAL_REQUIRED |
A human-only action (publish, send back, archive, delete) | Do it in the console |
| 404 | NOT_FOUND and resource-specific *_NOT_FOUND |
Unknown or foreign ID. The body is the same for both, so IDs cannot be probed | Check the ID |
| 409 | STALE_REVISION and limit codes |
A concurrent edit, or a questionnaire limit — see Supplier questionnaires API | Reload and retry, or stay within the limit |
| 413 | FILE_TOO_LARGE |
An SBOM upload over 5 MB | Upload a smaller manifest |
| 422 | QUERY_TOO_BROAD |
A lifecycle feed would return more rows than allowed — see Poll leaver items nightly | Narrow the filter |
| 422 | EXPIRY_REQUIRED, EXPIRY_TOO_LONG, SCOPE_UNAVAILABLE |
Token creation rules — see API tokens | Adjust the token |
| 429 | RATE_LIMITED |
A rate limit was hit. Honour Retry-After |
Wait, then retry once |
| 500 | INTERNAL_ERROR, DB_ERROR |
Unexpected server error | Retry later; quote error.id to support |
| 503 | BILLING_UNAVAILABLE |
The plan could not be checked | Retry later |
| 503 | AUDIT_UNAVAILABLE |
A response with names or emails could not be written to the audit trail, so it was withheld | Retry later |
Rate limits
| Limit | Applies to | Value |
|---|---|---|
| Edge limit | Every request, per client IP | 20 requests per second |
| Failed authentication | Per client IP | Repeated failures are blocked for a short while |
Lifecycle (/api/v1/jml/*) |
Per token | 5 requests per second sustained, burst of 30; plus an organisation-wide ceiling |
Lifecycle feeds (…/items) |
Per organisation | A small number of concurrent requests; excess gets Retry-After: 1 |
| Responses with names or emails | Per token, shared by REST, MCP employee_resolve and lifecycle |
3,000 identities per hour; see Employee identity |
| Questionnaire structural edits (create, clone, add or delete sections, questions, options, submit) | Per token / per person | 30 per hour / 300 per hour |
| Questionnaire field edits (meta, question, option, translate) | Per token / per person | 120 per hour / 1200 per hour |
| MCP | Per token and per organisation | See MCP errors & retries |
The supply-chain endpoints have no per-token limit beyond the edge limit. Per-token limits are design targets enforced per server instance, so the effective ceiling can be somewhat higher; do not build a design that depends on an exact number.
Rate-limit headers
Responses to token calls on rate-limited endpoints carry three headers, on success as well as on 429:
| Header | Meaning |
|---|---|
RateLimit-Limit |
The size of your token’s request allowance (its burst) |
RateLimit-Remaining |
Whole requests left right now |
RateLimit-Reset |
Seconds until the allowance is full again |
Use them to slow down before you hit a 429. A limit that is a daily row budget answers with Retry-After set to
the seconds left until 00:00 UTC. The three headers also appear on the supply-chain, supplier-template and
lifecycle endpoints, which now share one per-token allowance.
Handling 429
A 429 RATE_LIMITED response carries a Retry-After header in whole seconds (at least 1). Wait that long and retry
once; on repeated 429s back off exponentially. Never retry in a tight loop.
If several of your servers share one outbound IP address — a NAT gateway or a CI runner pool — one job that keeps failing authentication can briefly lock the others out. Give each integration its own valid token and fix failing jobs quickly.
Was this page helpful?