Storeshot API error codes

Every /v1 error body is { "error": { "code", "message", "requestId", "docUrl", "details?" } }. Reserved codes are part of the contract but not yet emitted.

INVALID_INPUT HTTP 400 · active

Invalid input. The request failed validation or its body could not be parsed. `details.issues` lists each problem as `{ location, path, message }`.

UNAUTHORIZED HTTP 401 · active

Unauthorized. No valid credential. Send `Authorization: Bearer <API key or access token>`. Missing, malformed, unknown, revoked and expired credentials all return this same body.

FORBIDDEN HTTP 403 · active

Forbidden. The credential is valid but may not do this. `details.reason` is one of `insufficient_scope` (with `details.requiredScope`, and `details.requiredScopes` when any of several scopes is accepted), `api_key_limit_reached` (with `details.limit`), `scope_not_grantable`, `environment_not_grantable`, `account_conflict`, `upload_quota_exceeded` (with `details.kind` `count` or `bytes` and `details.limit`; delete uploads to free space), `webhook_limit_reached` (with `details.limit`, webhooks per environment), `live_key_required` (billing checkouts need a live key or an access token; test keys never buy) or `billing_account_flagged` (a refund left the account short of credits it had already used; contact support before buying again).

NOT_FOUND HTTP 404 · active

Not found. No `/v1` endpoint exists at this path (`details` echoes `{ method, path }`), or the addressed resource does not exist for this caller.

METHOD_NOT_ALLOWED HTTP 405 · active

Method not allowed. The path exists but does not support this HTTP method. See the `Allow` header and `details.allowedMethods`.

PAYLOAD_TOO_LARGE HTTP 413 · active

Payload too large. The request body exceeds the endpoint limit. `details.maxBytes` gives the limit.

UPLOAD_NOT_READY HTTP 409 · active

Upload not ready. The upload cannot be used or completed in its current state. `details.reason` is `object_missing` (no file was uploaded to the presigned URL yet: upload it, then retry; the upload stays pending), `expired` (the upload expired before it was completed), `failed` (with `details.errorCode`) or `deleted`.

IDEMPOTENCY_CONFLICT HTTP 409 · active

Idempotency conflict. This `Idempotency-Key` was already used by this account for a different request (another body, or a credential of the other environment). `details.generationId` is the generation created with it (`details.screenshotSetId` for `POST /screenshot-sets`, `details.checkoutId` for `POST /billing/checkout`, also while that checkout is still being created). Use a new key for a new request.

GENERATION_STATE_CONFLICT HTTP 409 · active

Generation state conflict. The generation cannot do this in its current state. `details.status` is its status and `details.action` is `cancel` (only a generation that has not started yet can be canceled) or `delete` (only a finished generation can be deleted).

SCREENSHOT_SET_STATE_CONFLICT HTTP 409 · active

Screenshot set state conflict. The screenshot set cannot do this in its current state. `details.status` is the set status and `details.action` is `retry` (no image is failed or canceled, or every one used its 5 attempts; nothing was charged), `cancel` (the set already finished), `delete` (images are still queued or running: cancel the set or wait) or `archive` (no image has succeeded yet).

WEBHOOK_STATE_CONFLICT HTTP 409 · active

Webhook state conflict. The webhook cannot do this in its current state. `details.action` is `test` (a disabled webhook receives no test event: enable it first).

BILLING_STATE_CONFLICT HTTP 409 · active

Billing state conflict. The plan cannot do this in its current state; nothing was charged. `details.reason` is `subscription_exists` (the account already has a plan: change it instead), `checkout_pending` (a plan checkout is still open: finish it, or retry later), `no_subscription` (the account has no plan), `same_plan` (already on that plan), `canceling` (the plan ends at the period end: uncancel it first), `past_due` (a payment is overdue: update the payment method in the portal) or `no_customer` (no payment account is linked to this account yet).

IMAGE_TOO_LARGE HTTP 413 · active

Image too large. An uploaded image is too large. `details.reason` is `bytes` (the file exceeds `details.maxBytes`, 20 MB) or `pixels` (more than `details.maxPixels` pixels, 50 megapixels, or an edge longer than `details.maxEdge`, 16384 pixels).

UNSUPPORTED_FORMAT HTTP 415 · active

Unsupported format. An uploaded file is not a PNG, JPEG or WebP image (checked from its bytes, never from its name or declared type). `details.reason` is `unsupported_type`, `heic` (HEIC/HEIF photos are not supported; export as PNG or JPEG), `corrupt` (the image could not be decoded) or `type_mismatch` (the uploaded file is not the `contentType` declared when presigning).

NOT_A_SCREENSHOT HTTP 422 · active

Not a screenshot. The input image does not look like an app screenshot: its size or shape is not a screen (checked when you create the generation; `details.path` names the input), or it carries camera capture data such as an exposure time (checked before any image is generated; the generation fails with this `error.code` and its credit is refunded). Upload a screenshot taken on the device.

URL_FETCH_FAILED HTTP 422 · active

URL fetch failed. The image URL could not be fetched. Only `https://` URLs on port 443 that resolve to public addresses are fetched, with at most 3 redirects and 15 seconds in total. `details.reason` is `blocked_destination`, `dns_failure`, `connect_failed`, `tls_error`, `timeout`, `too_many_redirects`, `insecure_redirect` (a redirect to a non-https URL), `http_status` (with `details.status`) or `empty_body`.

WEBHOOK_URL_REJECTED HTTP 422 · active

Webhook URL rejected. The webhook URL is well formed but cannot be used. `details.reason` is `blocked_destination` (the host is or resolves to a private, loopback, link-local or otherwise non-public address; every DNS answer must be public) or `dns_failure` (the host does not resolve). The same check runs again before every delivery.

INSUFFICIENT_CREDITS HTTP 402 · active

Insufficient credits. The account does not have enough credits for this operation; nothing was created or charged. `details.requiredCredits` is the cost, `details.availableCredits` the current balance and `details.topUpUrl` where to get more credits. Test keys (`ss_test_…`) never need credits.

RATE_LIMITED HTTP 429 · active

Rate limited. Too many requests. Retry after the number of seconds in the `Retry-After` header (`details.retryAfterSeconds`). `details.scope` is `key` or `user` and `details.policy` is `requests` (per-minute request rate of the API key or of the whole account, with `details.limit` and `details.windowSeconds`; see the `RateLimit` headers) `daily_generations` (the account reached its generations for this UTC day, `details.limit`), `webhook_tests` (test events of one webhook per hour, `details.limit`) or `billing_checkouts` (checkouts created by the account in the last hour, `details.limit`). Without `details.scope`, too many failed authentication attempts came from this IP.

CONCURRENCY_LIMITED HTTP 429 · active

Concurrency limited. Too many generations of this account are in progress. Retry after the number of seconds in the `Retry-After` header (`details.retryAfterSeconds`); `details.limit` is the number allowed at once (`details.scope` `user`, `details.policy` `concurrent_generations`).

INTERNAL_ERROR HTTP 500 · active

Internal error. An unexpected error occurred. Quote the `requestId` when contacting support.

GENERATION_FAILED HTTP 502 · active

Generation failed. Appears as the `error.code` of a failed generation (never as an HTTP status): the image could not be generated. Its credit was refunded; create a new generation to try again.

SERVICE_UNAVAILABLE HTTP 503 · active

Service unavailable. A dependency is unavailable (on `/v1/health`, `details` contains the health report), or uploads are busy (`Retry-After`): retry after that many seconds.

BILLING_UNAVAILABLE HTTP 503 · active

Billing unavailable. Credits cannot be bought right now; nothing was charged. `details.reason` is `disabled` (web payments are switched off), `not_configured` (payments are not set up on this server), `provider_error` (the payment provider did not answer; retry later, with the same `Idempotency-Key` if you sent one) or `webhook_not_configured` (payment notifications are not accepted yet).

GENERATION_TIMEOUT HTTP 504 · active

Generation timeout. Appears as the `error.code` of a failed generation (never as an HTTP status): generating the image took too long. Its credit was refunded; create a new generation to try again.