> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alterscope.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> The Alterscope API error envelope, status codes, per-code reference, and retry guidance.

When a request fails, the API returns a non-2xx HTTP status and a JSON body wrapping a single `error` object. The API does **not** use the RFC 9457 `application/problem+json` shape — it returns the envelope below with `Content-Type: application/json`.

## Error envelope

```json theme={null}
{
  "error": {
    "code": "insufficient_scope",
    "message": "The API key does not have the required scope for this operation",
    "details": { "...": "optional, context-specific" },
    "request_id": "req_…",
    "documentation_url": "https://docs.alterscope.org/errors/insufficient_scope"
  }
}
```

| Field               | Always present | Meaning                                                                                                                                                   |
| ------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code`              | Yes            | A stable, machine-readable error code (branch on this, not the message).                                                                                  |
| `message`           | Yes            | A human-readable description. May change; do not parse it.                                                                                                |
| `request_id`        | Yes            | Unique per request. Quote it in support tickets. Also returned in the `X-Request-ID` response header.                                                     |
| `details`           | No             | Optional structured context for the error.                                                                                                                |
| `documentation_url` | No             | Link to this page's section for the code, when available. Resolves to `https://docs.alterscope.org/errors/<code>`, i.e. the matching anchor on this page. |

<Note>
  The `documentation_url` is emitted for the authentication and request-level error codes (the [Status codes](#status-codes) table below). Some specialized surfaces — feature-gated endpoints and the events replay endpoint — return their own error shapes documented under [Feature-gated and endpoint-specific errors](#feature-gated-and-endpoint-specific-errors). In every case, **branch on `code`, never on the HTTP status or message alone.**
</Note>

## Status codes

Each row's `code` is also a section anchor on this page, so `https://docs.alterscope.org/errors/<code>` deep-links here.

| Code                                                                | HTTP | When                                                                                            | Remediation                                                                                                                                       |
| ------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| <a id="missing_auth" />`missing_auth`                               | 401  | No `Authorization: Bearer` header was sent.                                                     | Send `Authorization: Bearer sk_live_…`. See [Authentication](/develop/get-started/authentication).                                                |
| <a id="unauthenticated" />`unauthenticated`                         | 401  | Authentication is required and was not satisfied.                                               | Provide a valid key. Browser WebSocket clients pass it via `?token=`.                                                                             |
| <a id="invalid_key" />`invalid_key`                                 | 401  | The API key is malformed or not recognized.                                                     | Re-issue a key in the [Developer Portal](https://app.alterscope.org/connections) and replace the value.                                           |
| <a id="expired_key" />`expired_key`                                 | 401  | The API key has expired.                                                                        | Mint a new key and rotate. See [Rotation and revocation](/develop/get-started/authentication#rotation-and-revocation).                            |
| <a id="revoked_key" />`revoked_key`                                 | 401  | The API key has been revoked.                                                                   | Mint a new key; the revoked one fails closed permanently.                                                                                         |
| <a id="insufficient_scope" />`insufficient_scope`                   | 403  | The key is valid but lacks the [scope](/develop/get-started/scopes) for this operation.         | Mint a key with the required scope (`details.required_scope` names it).                                                                           |
| <a id="forbidden" />`forbidden`                                     | 403  | The key may not access this resource.                                                           | Confirm the resource belongs to your organization.                                                                                                |
| <a id="invalid_request" />`invalid_request`                         | 400  | The request was malformed (bad JSON, wrong method, missing path parameter).                     | Fix the request shape, then retry.                                                                                                                |
| <a id="validation_failed" />`validation_failed`                     | 400  | The request parsed but failed field validation.                                                 | Read `message`/`details` for the offending field and correct it.                                                                                  |
| <a id="not_found" />`not_found`                                     | 404  | The requested resource does not exist.                                                          | Check the path and resource ID. Do not retry blindly.                                                                                             |
| <a id="conflict" />`conflict`                                       | 409  | The request conflicts with current state (e.g. the resource already exists).                    | Reconcile state — fetch the existing resource or change the identifier. See also [idempotency](#idempotency-in-flight-409) for the in-flight 409. |
| <a id="rate_limit_exceeded" />`rate_limit_exceeded`                 | 429  | Too many requests for your tier's per-minute limit.                                             | Wait for `Retry-After`, then retry. See [Rate-limit responses](#rate-limit-responses).                                                            |
| <a id="rate_limited" />`rate_limited`                               | 429  | Rate limit exceeded (returned by the authentication layer).                                     | Same as `rate_limit_exceeded`: honour `Retry-After` and back off.                                                                                 |
| <a id="risk_dependency_unavailable" />`risk_dependency_unavailable` | 503  | A risk data dependency (VaR, protocol risk, liquidity, correlation) is temporarily unavailable. | Retry with backoff; the upstream data source is transiently down.                                                                                 |
| <a id="unavailable" />`unavailable`                                 | 503  | The endpoint or a backing service is temporarily unavailable.                                   | Retry with backoff per [Retries](/develop/get-started/retries).                                                                                   |
| <a id="internal_error" />`internal_error`                           | 500  | An unexpected server error.                                                                     | Safe to retry with backoff. Quote `request_id` if it persists.                                                                                    |

## Rate-limit responses

A `429` from the per-minute limiter additionally sets:

```http theme={null}
Retry-After: 60
X-RateLimit-Limit: <burst>
X-RateLimit-Remaining: 0
X-RateLimit-Reset: <unix timestamp>
```

The `error.code` is `rate_limit_exceeded`. Wait for the `Retry-After` period (seconds) before retrying. The `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` headers are returned on **every** response, not just 429s, so you can pace yourself before you hit the limit. See [Rate limits](/develop/get-started/rate-limits) for per-tier limits.

## Feature-gated and endpoint-specific errors

A few endpoints enforce plan tiers, monthly quotas, or a tier-scoped data window. These return codes specific to the endpoint. They are **not** general status codes — only the listed endpoints emit them.

### Plan-gated and quota codes

These appear on the exit Monte Carlo (`/v2/liquidity/{id}/exit-monte-carlo`) and simulator (`/v2/simulator/...`) endpoints.

| Code                                                                          | HTTP | When                                                  | Remediation                                                                              |
| ----------------------------------------------------------------------------- | ---- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| <a id="MC_TIER_REQUIRED" />`MC_TIER_REQUIRED`                                 | 403  | Exit Monte Carlo requires a higher plan tier.         | Upgrade your plan. See [Rate limits](/develop/get-started/rate-limits) for tier details. |
| <a id="MC_MONTHLY_QUOTA_EXCEEDED" />`MC_MONTHLY_QUOTA_EXCEEDED`               | 429  | Your monthly exit Monte Carlo run quota is exhausted. | Wait for the next billing cycle or upgrade.                                              |
| <a id="SIMULATOR_TIER_REQUIRED" />`SIMULATOR_TIER_REQUIRED`                   | 403  | Simulator runs require a higher plan tier.            | Upgrade your plan. `details.required_plans` lists the eligible tiers.                    |
| <a id="SIMULATOR_MONTHLY_QUOTA_EXCEEDED" />`SIMULATOR_MONTHLY_QUOTA_EXCEEDED` | 429  | Your monthly simulator run quota is exhausted.        | Wait for the next billing cycle or upgrade.                                              |
| <a id="SIMULATOR_SCENARIO_NOT_FOUND" />`SIMULATOR_SCENARIO_NOT_FOUND`         | 404  | The requested simulator scenario does not exist.      | Check the scenario ID against the scenario catalogue.                                    |

<Note>
  The plan-gated and quota codes use UPPER\_SNAKE\_CASE and carry context in `details` (for example `details.required_plans`, `details.limit`, `details.upgrade_url`). They do not set `documentation_url`. Tier names are always one of Free / Analyst / Team / Enterprise / Custom — see [Rate limits](/develop/get-started/rate-limits) for per-tier limits.
</Note>

### Events replay errors

The events replay endpoint (`GET /v2/events/replay`) enforces a tier-scoped replay window. Its error body is a **flat** shape — `{"error": "<CODE>", "message": "…", "details": { … }}` — rather than the nested `error.code` envelope above. Branch on the top-level `error` string.

| Code                                                    | HTTP | When                                                                           | Remediation                                                                                              |
| ------------------------------------------------------- | ---- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| <a id="REPLAY_NOT_AVAILABLE" />`REPLAY_NOT_AVAILABLE`   | 404  | Event replay is not available on your tier (the tier's replay window is zero). | Upgrade to a tier that includes replay.                                                                  |
| <a id="REPLAY_WINDOW_EXPIRED" />`REPLAY_WINDOW_EXPIRED` | 410  | The `since_event_id` cursor is older than your tier's replay window.           | Start from a more recent cursor, or upgrade for a longer window. `details.window_days` names the window. |
| <a id="INVALID_CURSOR" />`INVALID_CURSOR`               | 400  | `since_event_id` is not a valid ULID.                                          | Pass a ULID cursor from a prior response's `next_cursor`.                                                |
| <a id="INVALID_EVENT_TYPE" />`INVALID_EVENT_TYPE`       | 400  | The `type` filter is not a recognized event type.                              | Use a valid event type. See [Webhooks](/develop/realtime/webhooks) for the event catalogue.              |

### Idempotency in-flight (409)

Endpoints protected by idempotency accept an `X-Idempotency-Key` header. If a request with the same key is still being processed by another worker, the API responds with `409 Conflict` and a `Retry-After: 1` header. The body is a flat `{"error": "duplicate request in flight; retry after a brief delay"}`.

* A **replay** of a completed request (same key, within the cache window) returns the original cached response with an `X-Idempotency-Replayed: true` header — not a 409.
* An **in-flight** duplicate returns the 409 above. Wait the `Retry-After` interval, then retry with the same key.

See [Idempotency](/develop/get-started/idempotency) for the full contract.

## Retry guidance

* **`429`** — back off for the `Retry-After` interval, then retry. For idempotent writes, reuse the same `X-Idempotency-Key`.
* **`500` and other 5xx** (including `503` `unavailable` / `risk_dependency_unavailable`) — retry with exponential backoff and a capped number of attempts.
* **`400`, `401`, `403`, `404`** — do not retry blindly; fix the request, key, scope, or path first.
* **`409`** — for an idempotency in-flight conflict, retry after the short `Retry-After`. For a resource `conflict`, reconcile state before retrying.

Always log `request_id` (or the `X-Request-ID` header) so support can trace a specific failed call.
