> For the complete documentation index, see [llms.txt](https://docs.maetra.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maetra.io/getting-started/errors-and-rate-limits.md).

# Errors & rate limits

### Error format

Every error response uses the same JSON shape and an appropriate HTTP status code:

```json
{
  "statusCode": 403,
  "code": "FORBIDDEN",
  "message": "Missing required scope: govern:checkpoints:write"
}
```

| Field        | Description                                 |
| ------------ | ------------------------------------------- |
| `statusCode` | The HTTP status code, repeated in the body. |
| `code`       | A stable, machine-readable error code.      |
| `message`    | A human-readable description, safe to log.  |

#### Status codes

| Status                  | When it happens                                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`       | A required field is missing or invalid — e.g. `action` omitted on a checkpoint, or `tool_name` omitted on a `tool_call` scan. |
| `401 Unauthorized`      | The API key is missing, malformed, revoked, or used from a disallowed IP.                                                     |
| `403 Forbidden`         | The key is valid but lacks the required scope.                                                                                |
| `404 Not Found`         | The referenced resource (e.g. a checkpoint ID) does not exist in this workspace.                                              |
| `429 Too Many Requests` | The rate limit was exceeded. Honor the `Retry-After` header.                                                                  |
| `500`                   | An unexpected server error. The `message` is generic; retry with backoff.                                                     |

### Rate limits

The API enforces a **sliding-window, per-key** limit.

* **Default:** 600 requests per minute per API key.
* **Response when exceeded:** `429 Too Many Requests` with a `Retry-After` header (seconds to wait).

```
HTTP/1.1 429 Too Many Requests
Retry-After: 12
```

#### Polling etiquette

When polling a checkpoint with `GET /v1/checkpoints/{id}`, keep a **minimum of 1 second** between polls of the same checkpoint — tighter polling is rejected. Prefer the long-poll endpoint `GET /v1/checkpoints/{id}/wait`, which holds the connection open and returns as soon as the state changes, so you make far fewer requests.

### Idempotency

Write endpoints (`POST /v1/checkpoints`, `POST /v1/secure/scan`) accept an optional `Idempotency-Key` header so retries are safe:

```
Idempotency-Key: 4f1e9c2a-7b3d-4a1f-9c8e-2d6b0a5f1e33
```

Reusing the same key with a **different** request body is rejected — a key is bound to the payload it was first seen with. Use a fresh UUID per logical operation, and reuse it only when retrying that exact operation.
