When a request fails, the API returns a non-2xx HTTP status and a JSON body wrapping a singleDocumentation Index
Fetch the complete documentation index at: https://docs.alterscope.org/llms.txt
Use this file to discover all available pages before exploring further.
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
| 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. |
The
documentation_url is emitted for the authentication and request-level error codes (the 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. In every case, branch on code, never on the HTTP status or message alone.Status codes
Each row’scode is also a section anchor on this page, so https://docs.alterscope.org/errors/<code> deep-links here.
| Code | HTTP | When | Remediation |
|---|---|---|---|
missing_auth | 401 | No Authorization: Bearer header was sent. | Send Authorization: Bearer sk_live_…. See Authentication. |
unauthenticated | 401 | Authentication is required and was not satisfied. | Provide a valid key. Browser WebSocket clients pass it via ?token=. |
invalid_key | 401 | The API key is malformed or not recognized. | Re-issue a key in the Developer Portal and replace the value. |
expired_key | 401 | The API key has expired. | Mint a new key and rotate. See Rotation and revocation. |
revoked_key | 401 | The API key has been revoked. | Mint a new key; the revoked one fails closed permanently. |
insufficient_scope | 403 | The key is valid but lacks the scope for this operation. | Mint a key with the required scope (details.required_scope names it). |
forbidden | 403 | The key may not access this resource. | Confirm the resource belongs to your organization. |
invalid_request | 400 | The request was malformed (bad JSON, wrong method, missing path parameter). | Fix the request shape, then retry. |
validation_failed | 400 | The request parsed but failed field validation. | Read message/details for the offending field and correct it. |
not_found | 404 | The requested resource does not exist. | Check the path and resource ID. Do not retry blindly. |
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 for the in-flight 409. |
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_limited | 429 | Rate limit exceeded (returned by the authentication layer). | Same as rate_limit_exceeded: honour Retry-After and back off. |
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. |
unavailable | 503 | The endpoint or a backing service is temporarily unavailable. | Retry with backoff per Retries. |
internal_error | 500 | An unexpected server error. | Safe to retry with backoff. Quote request_id if it persists. |
Rate-limit responses
A429 from the per-minute limiter additionally sets:
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 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 |
|---|---|---|---|
MC_TIER_REQUIRED | 403 | Exit Monte Carlo requires a higher plan tier. | Upgrade your plan. See Rate limits for tier details. |
MC_MONTHLY_QUOTA_EXCEEDED | 429 | Your monthly exit Monte Carlo run quota is exhausted. | Wait for the next billing cycle or upgrade. |
SIMULATOR_TIER_REQUIRED | 403 | Simulator runs require a higher plan tier. | Upgrade your plan. details.required_plans lists the eligible tiers. |
SIMULATOR_MONTHLY_QUOTA_EXCEEDED | 429 | Your monthly simulator run quota is exhausted. | Wait for the next billing cycle or upgrade. |
SIMULATOR_SCENARIO_NOT_FOUND | 404 | The requested simulator scenario does not exist. | Check the scenario ID against the scenario catalogue. |
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 for per-tier limits.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 |
|---|---|---|---|
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. |
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. |
INVALID_CURSOR | 400 | since_event_id is not a valid ULID. | Pass a ULID cursor from a prior response’s next_cursor. |
INVALID_EVENT_TYPE | 400 | The type filter is not a recognized event type. | Use a valid event type. See Webhooks for the event catalogue. |
Idempotency in-flight (409)
Endpoints protected by idempotency accept anX-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: trueheader — not a 409. - An in-flight duplicate returns the 409 above. Wait the
Retry-Afterinterval, then retry with the same key.
Retry guidance
429— back off for theRetry-Afterinterval, then retry. For idempotent writes, reuse the sameX-Idempotency-Key.500and other 5xx (including503unavailable/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 shortRetry-After. For a resourceconflict, reconcile state before retrying.
request_id (or the X-Request-ID header) so support can trace a specific failed call.