Skip to content
FolioSign inStart your trial

Docs · Concepts

Idempotency

How Idempotency-Key makes retries safe, what is replayed, and the three ways a key can be refused.

Send Idempotency-Key: <opaque string, at most 255 characters> on any POST, PUT, PATCH or DELETE. If a request times out or you are unsure whether it landed, send it again with the same key: Folio returns the first response instead of doing the work twice.

curl -X POST "$FOLIO_API_URL/v1/teachings" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sunday-2026-11-01-create" \
  -d '{
  "title": "Sunday service",
  "filename": "service.mp3",
  "content_type": "audio/mpeg"
}'

Rules#

  • Scope. A key is scoped to the credential that made the request (the API key, session or device key) and to the method and path. Two credentials can use the same string without seeing each other's responses. Requests without a valid credential are never stored.
  • Same key, same body. The stored response is replayed for 24 hours, with the header Idempotency-Replayed: true.
  • Same key, different body. 422 validation with error.meta.reason of idempotency_key_reused. Use a new key for a different request.
  • Same key while the first request is still running. 409 conflict with error.meta.reason of idempotency_in_progress and a Retry-After header. Wait, then send the same request again.
  • Not stored. Responses with status 401, 403, 429 or 5xx are not stored, so a retry runs the request again.
  • Secrets are never replayed. A response that carries a one-time secret (a new key, a webhook signing secret, a device key) is not stored. Replaying such a request answers 409 with error.meta.reason of idempotency_secret_unavailable. Use a new key to mint another secret.
  • Abandoned requests. A request that never completes is released after two minutes.

Choosing keys#

Derive the key from the thing you are doing, not from a random value you lose on retry: sunday-2026-11-01-create for the Sunday upload, invoice-1042-endpoint for an invoice's webhook. A random UUID generated once per logical operation and stored with it works too.

Verified against API version 0.0.0-dev. Generated from the live OpenAPI document.