Skip to main content
An idempotency key is a client-chosen string you send with a write request. The API stores the first response under the key and replays it for later requests carrying the same key. If a response is lost, for example after a timeout or a dropped connection, a retry with the same key returns that stored response and does not apply the operation again. Use a key on every request that creates or changes money-bearing objects: subscriptions, invoices, credit grants, checkout sessions. The API does not store a 5xx or 429 response. It releases the key, so a retry after one of these runs the request again. See Error handling.
POST /events, POST /credits/track and POST /credits/check work differently: the events in the request body identify the work, and the SDKs send no automatic key for them. See Writes identified by their events.

How it works

Add an Idempotency-Key header with a unique value. A repeat of the same request with the same key within 24 hours returns the original response without re-executing the operation.
Every mutating SDK method except events.ingest accepts { idempotencyKey } as its trailing options argument. If you set none, the SDK generates a key and sends it on every retry of that call. events.ingest, credits.track and credits.check are the exceptions: the SDK generates no key for them.

Writes identified by their events

Three writes take their identity from the request body, not from a generated key: The SDKs send no automatic Idempotency-Key on these three calls, and they retry them without one. A generated key is new on every call. On credits.track it would replace the unique_ids, so a queue that delivers the same events twice would draw credit twice. If you pass your own key, the SDK sends it and reuses it on each retry. Pass one to open a hold, or to replay a specific response.

Key behaviours

Response headers

A response to a request that carried an idempotency key includes: When a request fails with a 5xx or 429, the response also includes: When the first request with the same key is still being processed, the 409 response includes:

Error handling

  • 2xx — stored and replayed on retry
  • 4xx (validation, not found, and so on) — stored and replayed; these outcomes are deterministic, so a retry with the same parameters would fail the same way
  • 429 — not stored; the key is released so you can retry after backing off
  • 5xx — not stored; the key is released, so a retry with the same key runs the request again. A 500 may have changed data before it failed, so the SDKs retry 502, 503 and 504 but not 500. See Retrying.
See Errors for what each status means.

Key format

  • At most 255 characters
  • Printable ASCII only
  • Any scheme works; a short prefix plus a stable business identifier is easy to read in logs, for example ik_signup_<user_id> or a UUID
Scope keys to one operation: do not reuse a key across endpoints, and do not derive it from values that change between retries (such as the current time).

Expiry

Keys expire 24 hours after first use. After that the same string can be used for a new operation.

Which endpoints support it

Every mutating endpoint — POST, PUT, PATCH and DELETE — accepts Idempotency-Key. GET requests are idempotent by nature and ignore the header. The one exception is POST /events, which uses the per-event unique_id instead. POST /credits/track and POST /credits/check accept a key, but the SDKs do not generate one for them. See Writes identified by their events.
  • Errors — status codes, request IDs and which failures are safe to retry
  • API overview — headers, versioning and base URLs
  • Developer quick start — idempotent subscription creation in context