Skip to main content

Idempotency

Network requests fail. Clients retry. Without a deduplication contract, a retried POST quietly creates two records. The Scripe API gives every write endpoint a single contract for safe retries: pass an Idempotency-Key header. The server records the response under that key for 24 hours and replays it verbatim on subsequent requests with the same body — including the original status code.

How to use it

Generate a unique key per logical operation (a UUID is the obvious default — anything 1–64 chars matching [A-Za-z0-9_-] works) and send it on every retry of that operation:
The first request runs the handler. The response includes:
Within the next 24h, the same key + same body returns the cached response:
Your application logic does NOT need to special-case the replay path — the body is the original 200 envelope.

Validation rules

  • Key shape: 1–64 chars, regex ^[A-Za-z0-9_-]{1,64}$. Anything longer or with disallowed characters is silently ignored (treated as if no header was sent).
  • Scope: keys are scoped to (workspaceId, route). The same value can be reused safely across different endpoints.
  • Body hashing: we hash the canonicalised JSON body of the request (object keys sorted, no non-significant whitespace) with SHA-256. The hash, NOT the body itself, is what we compare on replay.
  • Window: 24 hours from the first request. After expiry the key can be reused with any body.

Conflict (409 idempotency_key_conflict)

If you send the same Idempotency-Key with a different body inside the 24h window, the server rejects the request:
Practical fix: generate a fresh key when you mean a fresh operation. Reuse a key only when retrying a literal retry of an unchanged body.

Body size cap

Buffering the request body for hashing has an upper bound (256 KB by default; some endpoints set their own limit). Bodies larger than the cap return 413 payload_too_large even when an Idempotency-Key is sent. The cap on each endpoint matches its content cap, so this only fires on truly oversize inputs.

What’s NOT idempotent

  • GET endpoints. They’re read-only — Idempotency-Key on a GET is ignored.
  • Side effects you trigger via webhooks fired by the API (the webhook delivery itself has its own idempotency layer: receivers should dedupe on event.id).
  • Background jobs queued by POST /v1/posts/generations (coming in a later release) — those expose their own job-id-based idempotency.

When to use it (always, but here’s the threshold)

Whenever the cost of a duplicate write is non-zero. Notes and posts both fall into that category — a duplicate post in the customer’s queue is annoying. We strongly recommend including an Idempotency-Key on every write request as a default.