API guide

Idempotency

Idempotency

Use idempotency keys for write requests that may be retried by your backend.

Header

Idempotency-Key: order_123_stackfill_session

StackFill enforces at-most-once execution per (team, key) pair. The first write with a given key runs; any subsequent request with the same key replays the cached outcome — body and status are byte-identical to the original response. Keys are held for 24 hours from first use.

Concurrency contract

Two requests with the same Idempotency-Key that arrive at the same time are arbitrated atomically. Exactly one runs the mutation:

  • The winner runs the handler and its response is cached.
  • The loser receives either the cached response (if the winner has already completed) or 409 idempotency_request_in_progress (if the winner is still running). Retry the 409 after a short delay to pick up the cached outcome.

This is implemented with a durable D1-backed claim, not a best-effort marker — true at-most-once even under high concurrency.

Error retries

A handler that returns an error response (4xx or 5xx) also caches the outcome. A retry with the same key replays the original error. This is intentional: a POST /v1/renders that 5xx'd may have already partially executed (R2 bytes written, fonts embedded, Stripe customer created), and silently re-running on the same key would compound the side effect.

If you want a fresh attempt after a server-side failure, use a new key for the retry. The original key's snapshot remains available for replay until the 24h TTL expires.

Example

curl https://api.stackfill.com/v1/embed/sessions \
  -H "Authorization: Bearer stackfill_live_sk_REPLACE_ME" \
  -H "Idempotency-Key: cart_123_personalization" \
  -H "Content-Type: application/json" \
  -d '{"external_product_id":"business-card"}'

The response carries Idempotent-Replay: true on every replay and Idempotent-Replay: false on the original execution.

Common errors

Code Status When
idempotency_key_mismatch 422 Same key reused with a different request body
idempotency_key_too_long 400 Header exceeds 255 characters
idempotency_request_in_progress 409 Another request with this key is still being processed; retry shortly
idempotency_store_unavailable 503 Internal claim store is temporarily unreachable; retry shortly

See Errors.