API guide

Errors

Errors

Use this guide to handle failed API responses consistently. Every /v1/* error follows the same envelope, and every doc_url in that envelope links into the reference table on this page.

Error envelope

{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_fields",
    "message": "template_id and scene_revision are required.",
    "doc_url": "https://stackfill.com/docs/errors/missing_fields",
    "request_id": "req_123"
  }
}

Fields:

  • type — high-level family: authentication_error, permission_error, invalid_request_error, not_found_error, plan_limit_error, rate_limit_error, api_error, service_unavailable.
  • code — stable machine-readable identifier. Stable across API versions; safe to switch on.
  • message — human-readable summary. May change wording without notice.
  • doc_url — deep-links to the matching row in the reference on this page.
  • request_id — repeat back to support to look up the offending request.

Common status codes

Status Meaning
400 Invalid JSON, missing fields, or invalid parameters.
401 Missing or invalid API key.
402 Plan or entitlement limit.
403 Origin, scope, or permission failure.
404 Resource not found for this team.
409 Conflict, usually duplicate mapping or wrong state.
410 Gone — session/demo/fill has expired.
413 Upload exceeds the per-asset size cap.
415 Unsupported media type.
422 Validation or idempotency mismatch.
429 Rate limit exceeded.
500 Unexpected API error. Safe to retry once after a short delay.
502 Renderer container unreachable or failed.
503 Subsystem (D1/KV/R2/Stripe/webhook secrets) not configured.

Common handling

  • Show user-recoverable copy for 400, 402, 403, 404, 409, 410, 413, 415, 422.
  • Retry only 429 and transient 5xx, and respect Retry-After when present.
  • Treat 5xx as transient unless the same code repeats — *_not_configured 503s mean an operator needs to set a secret.
  • request_id is your friend. Log it on every failed call.

Looking up a specific code

doc_url URLs follow the shape https://stackfill.com/docs/errors/<code>. The platform redirects those to this page with the code as the URL fragment, so the row scrolls into view automatically. You can also use the in-page search (Cmd/Ctrl + F) to find a code.

Reference

Authentication (401)

Code Type What it means
authentication_required authentication_error No Authorization: Bearer <key> header. Send your API key.
invalid_api_key authentication_error Header present but malformed, unknown, or revoked.
api_key_expired authentication_error Key passed its expires_at. Rotate or create a new one.
api_key_rotated authentication_error Key was rotated more than 24 hours ago. Swap to the new key in Link: rel="successor-version".
invalid_publishable_key authentication_error Public widget routes only accept publishable (pk_) keys.
invalid_embed_session_secret authentication_error client_secret from the URL fragment doesn't match the session row.
embed_session_secret_required authentication_error Embed runtime called without a client_secret.
invalid_access_token authentication_error One-time hosted-fill access token is malformed or revoked.
session_required authentication_error sf_session cookie missing or expired on a /app or /admin route.
demo_expired authentication_error 24-hour demo workspace has expired. Sign up for a real plan.
sweep_unauthorized authentication_error /api/admin/webhooks/sweep needs platform admin or the WEBHOOK_SWEEP_SECRET bearer.

Permission / scope / origin (403)

Code Type What it means
insufficient_scope authentication_error Key authenticated but lacks the scope this endpoint requires.
origin_not_allowed authentication_error Browser request from an Origin not in the publishable key's allowlist.
ip_not_allowed authentication_error Secret key's IP allowlist blocked this caller.
publishable_key_required authentication_error Endpoint is browser-only — use a publishable (pk_) key.
secret_key_required authentication_error Endpoint is server-only — use a secret (sk_) key, not publishable.
access_token_mismatch permission_error Hosted-fill token doesn't authorize the requested fill.
platform_admin_required permission_error /admin + /api/admin/* are restricted to PLATFORM_ADMINS.

Plan / billing (402)

Code Type What it means
api_plan_required plan_limit_error Public API needs Pro+. Upgrade in the dashboard.
embed_plan_required plan_limit_error Storefront embed needs Pro+. Upgrade in the dashboard.
template_limit_reached plan_limit_error Plan's template cap is full. Archive one or upgrade.
subscription_inactive plan_limit_error Subscription past due. Update payment in the customer portal.
subscription_canceled plan_limit_error Subscription was canceled; period_end has passed. Resubscribe.

Not found (404)

These all mean "no record matches that ID for the calling team". 404 instead of 403 to avoid leaking existence to unrelated tenants.

Code Type What it means
not_found not_found_error Generic — path not handled by the API.
wrong_host not_found_error /v1/* is served at api.stackfill.com. The apex returns this hint.
template_not_found not_found_error Template ID missing or archived.
template_not_live not_found_error Template exists but isn't published to live. Call POST /v1/templates/:id/publish.
fill_not_found not_found_error Fill ID missing or expired.
render_not_found not_found_error Render ID missing for this team.
render_blob_missing not_found_error Render row exists but the R2 blob is gone (rare, contact support).
embed_application_not_found not_found_error Publishable key isn't linked to any embed app.
embed_session_not_found not_found_error Session ID missing or expired.
product_mapping_not_found not_found_error No mapping for this SKU/product_id.
webhook_endpoint_not_found not_found_error Endpoint ID missing or owned by another team.
webhook_delivery_not_found not_found_error Delivery row missing under that endpoint.
api_key_not_found not_found_error API key ID missing or revoked.
publishable_key_not_found not_found_error Publishable key not in the catalog.
font_not_found not_found_error Font asset missing for this team.
font_blob_missing not_found_error Font row exists but the R2 blob is gone.
image_asset_not_found not_found_error Image asset missing.
image_file_not_found not_found_error Image row exists but the R2 blob is gone.
scene_not_found not_found_error Template has no scene rows; ingest may have failed.
selection_set_not_found not_found_error Selection set ID missing.
brand_logo_not_found not_found_error Brand logo asset missing.
company_not_found not_found_error Company (CRM) ID missing.
client_user_not_found not_found_error Client (CRM) user missing.
project_not_found not_found_error Project ID missing.
team_not_found not_found_error Team ID missing.
user_not_found not_found_error User ID missing.
plan_not_found not_found_error Plan ID missing in the catalog.
billing_team_not_found not_found_error Stripe customer references a team we no longer have.
setting_not_found not_found_error Operator setting key not found.
access_token_expired not_found_error Hosted-fill one-time token already used or expired.

Bad request (400)

Almost always "you sent JSON we can't accept". The message field spells out which field. Read it once during integration, then drop the verbose check.

Code Type What it means
invalid_json invalid_request_error Request body didn't parse as JSON.
invalid_multipart invalid_request_error Multipart upload couldn't be parsed.
missing_fields invalid_request_error Required JSON fields missing.
missing_required_field invalid_request_error A single named field is missing.
missing_asset_fields invalid_request_error Asset upload missing kind/scope/file.
missing_asset_name invalid_request_error Asset name field missing.
missing_allowed_origins invalid_request_error Publishable key needs at least one allowed origin.
invalid_mode invalid_request_error mode must be live or test.
invalid_kind invalid_request_error Asset kind outside the allowed enum.
invalid_scene invalid_request_error Scene JSON failed validation.
invalid_plan invalid_request_error Plan ID not in the catalog or not active.
invalid_billing_interval invalid_request_error interval must be monthly or annual.
invalid_pricing_mode invalid_request_error Pricing mode unrecognized.
invalid_features_json invalid_request_error Plan feature JSON failed validation.
invalid_limits_source invalid_request_error Limits-source field unrecognized.
invalid_setting_key invalid_request_error Operator setting key outside the allowlist.
invalid_setting_value invalid_request_error Operator setting value rejected by validator.
invalid_setting_scope invalid_request_error Operator setting scope rejected.
invalid_application_status invalid_request_error Embed app status outside active/paused/archived.
invalid_asset_scope invalid_request_error Asset scope not team or template.
invalid_client_email invalid_request_error Client email field failed RFC 5322 validation.
invalid_client_user_status invalid_request_error Client-user status outside the allowlist.
invalid_company_status invalid_request_error Company status outside the allowlist.
invalid_key_mode invalid_request_error API key mode must be live or test.
invalid_key_status invalid_request_error API key status outside active/revoked.
invalid_key_type invalid_request_error API key type outside secret/publishable.
invalid_last_team invalid_request_error Cannot remove yourself from your only team.
invalid_mapping_status invalid_request_error Product mapping status outside the allowlist.
invalid_project_status invalid_request_error Project status outside the allowlist.
invalid_session_status invalid_request_error Embed session status outside the allowlist.
invalid_template_status invalid_request_error Template status outside the allowlist.
invalid_webhook_status invalid_request_error Webhook endpoint status outside active/paused/disabled.
invalid_webhook_url invalid_request_error Webhook URL must be https and absolute.
invalid_stripe_event invalid_request_error Stripe payload failed signature or shape check.
invalid_accent_color invalid_request_error Color value not a valid hex.
invalid_primary_color invalid_request_error Color value not a valid hex.
cannot_delete_self invalid_request_error Admins can't delete their own user from /admin.

Conflicts (409)

You're trying to write a row that conflicts with state or another row.

Code Type What it means
client_email_taken invalid_request_error Another client (CRM) record uses that email.
company_name_taken invalid_request_error Another company (CRM) uses that display name.
team_email_taken invalid_request_error Another team uses that contact email.
user_email_taken invalid_request_error Another user uses that email.
project_name_taken invalid_request_error Project name collides within the team.
plan_exists invalid_request_error Plan ID already in the catalog.
product_mapping_exists invalid_request_error Mapping for that external_product_id already exists.
setting_exists invalid_request_error Operator setting already present.
template_archived invalid_request_error Cannot mutate an archived template.
no_scene invalid_request_error Template has no scene; publish it after ingest finishes.
render_not_ready invalid_request_error Render is still pending; wait for succeeded.
delivery_already_succeeded invalid_request_error Webhook delivery already succeeded — no retry needed.
endpoint_disabled invalid_request_error Webhook endpoint disabled after 20 consecutive failures. Re-enable in the dashboard.
fill_expired invalid_request_error Fill's expires_at has passed.
fill_not_open invalid_request_error Fill is finalized or archived; reopen by creating a new one.
key_already_rotated invalid_request_error API key is already mid-rotation; wait for the grace window to end.
key_not_active invalid_request_error Cannot rotate a revoked or expired key.

Gone (410)

Code Type What it means
embed_session_closed invalid_request_error Embed session closed (saved/abandoned); start a new one.
embed_session_expired invalid_request_error Embed session past its TTL; start a new one.

Size limits (413)

Code Type What it means
font_too_large invalid_request_error Font file exceeds the per-asset cap.
image_too_large invalid_request_error Image file exceeds the per-asset cap.
pdf_too_large invalid_request_error PDF upload exceeds the per-template cap.

Media type (415)

Code Type What it means
unsupported_media_type invalid_request_error Request body's Content-Type isn't accepted here.
unsupported_font_binary invalid_request_error Font file isn't OTF/TTF/WOFF/WOFF2.
unsupported_image_type invalid_request_error Image file isn't PNG/JPG/SVG.

Validation / idempotency (422)

Code Type What it means
delivery_exhausted invalid_request_error Webhook delivery already hit its 10-attempt cap.
delivery_skipped invalid_request_error Delivery was skipped (endpoint paused at the time); not retryable.
publishable_no_rotate invalid_request_error Publishable keys don't support rotation; revoke and create a new one.
idempotency_key_mismatch idempotency_error Same Idempotency-Key reused with a different request body.

Idempotency conflicts (409)

Code Type What it means
idempotency_request_in_progress idempotency_error Another request with this Idempotency-Key is still being processed. Retry after a short delay to pick up the cached outcome.

Rate limit (429)

Code Type What it means
rate_limit_exceeded rate_limit_error Sliding-window rate limit tripped. Respect Retry-After.

Server errors (500 / 502 / 503)

Transient unless the same code repeats. *_not_configured 503s mean an operator needs to set a secret — see STRIPE-PRODUCTION-CHECKLIST.md or docs/operations/.

Code Type What it means
internal_error api_error Handler crashed before sending a normal response.
render_failed api_error Render pipeline crashed mid-job.
fill_render_failed api_error Finalize step couldn't produce output.
ingest_failed api_error PDF ingest crashed; check the templates log in /admin.
rotation_failed api_error API key rotation crashed mid-write.
scene_fetch_failed api_error Couldn't load the scene row for a template.
stripe_event_failed api_error Stripe webhook handler crashed; Stripe will retry.
access_token_corrupt api_error Hosted-fill token couldn't be decoded; create a new fill.
fill_context_missing api_error Fill row exists but linked template/scene is gone.
renderer_failed api_error Ghostscript renderer container returned an error.
renderer_unreachable api_error RENDERER_SERVICE_URL returned a network error.
fill_team_mismatch invalid_request_error Internal — fill's team doesn't match the calling key.
fill_template_mismatch invalid_request_error Internal — fill's template_id doesn't match the URL.
billing_not_configured invalid_request_error Plan catalog row missing both Stripe price IDs.
billing_plan_not_found invalid_request_error Plan row referenced by team is gone.
db_unavailable service_unavailable D1 binding missing.
kv_not_configured service_unavailable Required KV binding missing.
r2_not_configured service_unavailable Required R2 binding missing.
stripe_not_configured service_unavailable STRIPE_SECRET_KEY unset.
stripe_webhook_secret_missing service_unavailable STRIPE_WEBHOOK_SECRET unset.
webhook_secrets_not_configured service_unavailable Outbound webhook signing secrets unset.
idempotency_store_unavailable service_unavailable Internal idempotency-claim store unreachable; retry shortly.

See API reference for endpoint-level docs.