Developers · Errors & limits

Errors, rate limits & pagination

One error shape across every route, a per-key rate limit that fails closed, and cursor-based pagination for every list endpoint.

The envelope

Error shape

{ error: string, fields?: Record<string, string> } — Every error is JSON with a human-readable error message. 422 validation failures add a fields map of field-path → message.

A plain error

{
  "error": "Not found"
}

A 422 validation failure

{
  "error": "Validation failed",
  "fields": {
    "legalName": "Required",
    "lei": "Must be exactly 20 letters or digits"
  }
}
  • 429 responses set Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers.
  • Entity 412 revision conflicts add currentRevision to the body and set an ETag of the current revision.
  • The webhook-subscription 409 limit error adds limit: 25.
  • Datastore failures on the generic resource routes surface as 502 { error: "Bad Gateway" }; entity-command failures surface as 503.
  • Restricted records a key cannot access are reported as 404 Not found, indistinguishable from absent records.

Every condition

Status codes

Every distinct status/error/condition combination documented anywhere in the API, collapsed from the per-endpoint lists.

StatusErrorWhen
400Idempotency-Key header is required.The Idempotency-Key header is absent.
400Idempotency-Key header is required. | If-Match must contain one numeric entity revision.The Idempotency-Key header is absent, or If-Match is present but not a single non-negative integer.
400Invalid JSON body.The request body is not parseable JSON.
400Invalid pagination cursor.The cursor is malformed or was tampered with.
401UnauthorizedMissing, malformed, unknown, revoked, or expired API key (no detail is leaked about which).
403Forbidden: <reason>The key lacks the required scope, the workspace subscription is not an active Enterprise plan, workspace access is suspended, or the required module is not on the plan.
403Forbidden: manage_webhooks capability requiredThe key does not carry the manage_webhooks capability.
403Forbidden: The restricted_records capability is required to change record access.restricted or viewerIds supplied without the capability.
403Outbound webhooks require Enterprise.The workspace billing document is not an active/trialing Enterprise subscription.
404Not foundNo record with this id exists in the key's workspace (restricted records the key cannot access are indistinguishable from absent ones).
404Not foundThe entity does not exist, has no approved deletion request, or is a restricted record the key cannot access.
409A workspace may have at most 25 webhook subscriptions.The 25-subscription cap is reached. Body carries limit: 25.
409Entity conflict | Entity limit reached. | Idempotency key conflicts with an earlier request.A conflicting entity exists, the free-tier entity cap is reached, or the Idempotency-Key was already used with a different request body.
409Webhook subscription is not deliverable.The stored subscription fails integrity validation (disabled, malformed URL/events/secret, or wrong workspace).
412Entity revision does not match.If-Match does not equal the stored revision. Body carries currentRevision; ETag header carries the current revision.
413Request body too large.The JSON body exceeds 1 MiB (1,048,576 bytes).
422Validation failedThe body is not a plain JSON object, contains unknown keys, or a field fails its schema; a fields map names each offending field.
422Validation failedUnknown keys, bad URL, unknown/duplicate events, or recordAccess: restricted without the capability — fields map names each.
422Validation failedUnknown keys, no updatable fields, or a field fails validation.
428If-Match header is required.The If-Match header is absent.
429Rate limit exceededMore than 1,000 requests in the key's 15-minute window. Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers are set.
502Bad GatewayThe datastore read or write failed.
503Entity mutations are temporarily unavailable.The workspace's entity-mutation mode is transitioning (Retry-After: 5 is set).
503Entity state requires reconciliation.Persisted entity/receipt state failed integrity checks (logged for operators).
503Service unavailableAn unclassified server error occurred.
503The public API is not configured in this environment.The deployment has no API_KEY_PEPPER or Firebase Admin credentials (the feature is dormant).

Per key

Rate limits

1000 requests per 15 minutes, per API key. Per API key (a Firestore-transaction counter per key; in production an unavailable limiter fails CLOSED).

429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1752570600

{
  "error": "Rate limit exceeded"
}

Every response — success or failure — carries the key’s current standing via Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.

List endpoints

Pagination

{ data: T[], nextCursor: string | null } — pass ?cursor= from the previous page to fetch the next.

Request

curl "https://app.alethiahq.com/api/v1/entities?limit=50&cursor=eyJvZmZzZXQiOjUwfQ" \
  -H "Authorization: Bearer tk_{workspaceId}_{secret}"

Response

{
  "data": ["...up to 50 records"],
  "nextCursor": "eyJvZmZzZXQiOjUwfQ"
}
  • Default page size 50, maximum 200.
  • Collections are ordered by document id (stable, deterministic).
  • Pass nextCursor back as ?cursor= to fetch the following page; null means the end.
  • Cursors are OPAQUE. For keys without restricted_records they are AES-256-GCM-encrypted tokens; tampering yields 400 "Invalid pagination cursor."
  • A page may contain fewer than limit items even when nextCursor is non-null (restricted records are filtered after paging).
  • The webhook-subscriptions list is not paginated (capped at 25).

Building something that needs more headroom?

Talk to us about your expected volume — rate limits are per key, and a workspace can hold more than one.