# Errors & Rate Limits
URL: https://docs.autociso.io/developers/errors-and-limits/
Description: The REST response envelope, every error code an API token can receive, and the rate limits that apply per IP, per token and per organisation.

This page covers the REST API. MCP reports errors differently — inside a successful JSON-RPC response — and has its
own page: [MCP errors & retries](https://docs.autociso.io/developers/mcp/errors/).

## The response envelope

Every REST response has the same shape. On success `error` is `null`; on failure `data` is `null`:

```json
{ "data": { "…": "…" }, "error": null, "meta": { "limit": 25, "offset": 0, "total": 3 } }
```

```json
{ "data": null, "error": { "id": "a1b2c3d4", "code": "INSUFFICIENT_SCOPE", "message": "this API token does not have the required scope: sbom:write" } }
```

- `error.code` is stable and machine-readable. Branch on it, never on `message`.
- `error.message` is a human-readable explanation; it may change.
- `error.args`, when present, carries details — the invalid parameter, the allowed values, the plan a feature needs,
  or `retryAfterSeconds`.
- `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`](https://docs.autociso.io/api/reference/token-scopes/list/) |
| 400 | `INVALID_REQUEST` | A malformed multipart form | Fix the request |
| 400 | `MISSING_FILE`, `UNSUPPORTED_FORMAT`, `PARSE_ERROR` | SBOM upload problems — see [Upload an SBOM](https://docs.autociso.io/api/upload-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](https://docs.autociso.io/getting-started/plans-and-features/) 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](https://docs.autociso.io/api/reference/supplier-templates/list/) | 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](https://docs.autociso.io/api/poll-offboarding/) | Narrow the filter |
| 422 | `EXPIRY_REQUIRED`, `EXPIRY_TOO_LONG`, `SCOPE_UNAVAILABLE` | Token creation rules — see [API tokens](https://docs.autociso.io/developers/api-tokens/#expiry-rules) | 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 |

**Note — Two kinds of 403**

`FORBIDDEN` means the door is closed to API tokens entirely — adding scopes will not help. `INSUFFICIENT_SCOPE`
names the scope you need and is fixed by issuing a new token. See the [Access model](https://docs.autociso.io/developers/access-model/).

## 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/*`](https://docs.autociso.io/api/reference/)) | 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](https://docs.autociso.io/developers/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](https://docs.autociso.io/developers/mcp/errors/) |

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 `429`s 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.
