Meet the new Scripe, live on October 7.Register

API v1 · Errors

Error codes

Every Scripe API error returns a stable machine-readable code you can switch on. The rest of the body is human-readable and may evolve. The docs_url field on every error envelope deep-links to the entry for that code on this page; most codes also have a dedicated page with causes and remediation.

json
{
  "error": {
    "code": "key_revoked",
    "message": "This API key has been revoked.",
    "request_id": "req_01J9Z…",
    "docs_url": "https://docs.scripe.io/api/v1/errors#key_revoked"
  }
}

See Conventions § Errors for the envelope structure. The same codes are returned by the MCP tool error envelope, so one parser handles both surfaces. OAuth endpoints use the same envelope on their JSON legs and RFC 6749 query parameters on the redirect leg — see OAuth § Errors.

How to handle errors in your client

  1. Switch on code, not status or message. Codes are stable; wording is not. HTTP status is a coarse classifier.
  2. Always log request_id. It's the only thing that lets us correlate your incident with our server-side trace.
  3. Distinguish 401 vs 403 in your alerting. A 401 is a credentials issue (re-mint, rotate). A 403 is a permission issue (grant more scopes, or upgrade the plan). Wiring them to the same on-call channel is a common debugging dead-end.
  4. Respect 429. The Retry-After header is the smallest safe sleep value. Treat 429 as transient, never as a permanent failure.
  5. Surface docs_url. When showing an error to a developer, link straight to the relevant page.

400 — malformed requests

invalid_request

The request payload or a query parameter is malformed, or (on MCP) a confirmation token is invalid/expired — the details.reason field distinguishes confirmation_invalid / confirmation_expired. Details →

bad_cursor

The pagination cursor is invalid or no longer applies (usually because a filter changed mid-loop). Restart the loop. Details →

bad_pagination

limit is not a positive integer, or another pagination parameter is invalid. A limit above the endpoint's maximum is clamped, not rejected. Details →

version_unsupported

Scripe-Api-Version names a version this build doesn't serve. The message lists the accepted versions. Details →

ssrf_blocked

A webhook endpoint URL resolves to a private or restricted IP. Details →

401 — fix your authentication

unauthenticated

No Authorization header, or no recognisable Bearer token. Details →

invalid_token

The token format is invalid, or the token no longer maps to a live principal. Details →

key_revoked

The API key was revoked. Mint a new one. Details →

key_expired

The API key passed its expiresAt. Mint a new one. Details →

402 — AI budget exhausted

usage_limit_exceeded

The workspace's weekly AI usage limit is exhausted (the primary budget). details carries scope, percentage, and reset time. Details →

spend_cap_exceeded

The workspace's daily API spend cap is reached (the abuse valve). Resets 00:00 UTC. Details →

403 — permissions

scope_missing

The token lacks the scope this endpoint requires. Look the endpoint up in the scope catalogue rather than parsing the message. Details →

plan_not_eligible

The workspace's plan does not include the product feature this specific capability uses. API access itself is on every plan. Details →

workspace_mismatch

The token cannot access the requested workspace. Details →

forbidden_project

The principal cannot access the requested project (assignment-based reach). Details →

admin_required

The action needs workspace-admin authority, not just the scope. Details →

404 / 405

not_found

The resource does not exist OR is not visible to this workspace — the API never distinguishes the two. Details →

method_not_allowed

The route does not accept this HTTP method. The Allow header lists what it does accept. Details →

409 — state conflicts

conflict

The request conflicts with the resource's current state (dead LinkedIn connection on a scheduling write, publish already in flight, synced document refusing deletion). Details →

idempotency_key_conflict

The same Idempotency-Key was reused with a different request body inside the 24-hour window. Details →

not_cancellable

The job is already running, done, failed, or cancelled — only QUEUED jobs can be cancelled. Details →

endpoint_disabled

Reserved for a disabled webhook endpoint; no route returns it today. Details →

413 / 422

payload_too_large

The request body exceeded the per-endpoint size cap. Details →

unprocessable

Syntactically valid but semantically rejected (bad enum, past timestamp, cross-project reference). Details →

429

rate_limited

A rate-limit bucket is exhausted (or, on agent surfaces, the publish budget). Honour Retry-After. Details →

5xx — Scripe-side

internal_error

Unexpected server-side failure. Retry with backoff; check status.scripe.io. Details →

service_unavailable

A downstream subsystem a request depends on (S3, LinkedIn, Clerk) is unavailable. Not emitted by GET /v1/health, which reports degradation in its own body instead. Details →

session_capacity

Reserved for MCP session-capacity exhaustion; no route returns it today (the server evicts the oldest session instead). Details →

OAuth codes

Returned by the OAuth endpoints (envelope on JSON legs, query parameters on the redirect leg — see OAuth § Errors):

invalid_grant

Code expired/used, PKCE mismatch, or refresh token invalid/revoked. Details →

invalid_client

Client authentication failed — unknown client_id. Details →

invalid_client_metadata

The DCR registration body is invalid (e.g. a non-none auth method). Details →

invalid_redirect_uri

The redirect URI is not registered for this client or malformed. Details →

unsupported_grant_type

Only authorization_code and refresh_token are supported. Details →

unsupported_response_type

Only response_type=code is supported. Details →

invalid_scope

A requested scope is outside the closed list, or a refresh tried to widen scope. Details →

client_suspended

The OAuth client was suspended by Scripe. Details →

refresh_token_reuse

Rotation reuse detected — the token family was revoked. Details →

workspace_unavailable

The targeted workspace is not accessible to the consenting user. Details →

The consent is missing, expired, or revoked — re-authorize. Details →

access_denied

The user cancelled the consent screen. Details →