PATTERN Cited by 1 source
Idempotency-Key header¶
Pattern¶
Support an Idempotency-Key (or equivalent) header on
non-idempotent write endpoints. The caller supplies a unique
key per logical operation; the server deduplicates by key and
returns the cached original response on any replay. A
retry after an ambiguous failure becomes safe: the server has
seen the key before and either returns the stored result, or
finishes the original in-flight operation and returns its
result.
Zalando's timeouts post names the pattern as the enabling contract for safe retry on non-idempotent writes:
"For safely retrying requests without accidentally performing the same operation twice, consider supporting additional Idempotency-Key header in your API. When creating or updating an object, use an idempotency key. Then, if a connection error occurs, you can safely repeat the request without the risk of creating a second object or performing the update twice." (Source: sources/2023-07-25-zalando-all-you-need-to-know-about-timeouts)
Named public references: Stripe — Idempotent Requests and Amazon Builders' Library — Making retries safe with idempotent APIs.
Shape¶
On the client:
- Generate a fresh key per logical operation (UUID v4 is
typical).
- Attach the key as Idempotency-Key: <uuid> on the write
request.
- On retry (network error, timeout, 5xx), reuse the same
key.
- Reuse keys across retries, never across logical operations.
On the server:
- Store (key, request_hash, response, status) on first
receipt.
- On subsequent requests with the same key:
- If request body matches stored hash → return stored
response.
- If request body differs → reject with 409 Conflict (key
reuse for a different operation).
- Age out stored keys after a window (Stripe: 24 hours; AWS
products vary).
- Enforce some form of serialisation / locking so two
concurrent retries of the same key don't both execute.
Why key + hash, not just key¶
Storing only (key, response) creates a subtle bug: a client
that reuses a key for a different payload will receive the
wrong response. Storing (key, request_hash, response) and
rejecting mismatches with 409 Conflict turns key reuse into
a loud failure rather than a silent correctness hole.
Why storage, not just "first-one-wins"¶
First-one-wins — dedupe by key only on the first request — leaves a race: two near-simultaneous clients with the same key can both be in flight. Correct implementations either:
- Lock on key while the first request executes; second blocks until first commits, then returns the stored response.
- Optimistic insert with a unique constraint on key; the loser polls for the stored response.
Retention window¶
Idempotency storage is unbounded if keys are kept forever. Production implementations cap retention:
- Stripe: 24 hours.
- Many AWS services: 10 minutes to 24 hours.
Callers must complete retries within the retention window, or the key will be treated as fresh and the operation may re-execute. The retention bound, caller's retry bound, and service SLA must be consistent.
When to apply¶
- Always: payment operations, order creation, any write with user-visible side effect.
- Usually: account mutations, subscription changes, resource creation.
- Not strictly needed: idempotent writes by construction
(
PUTfull replacement, set-valued writes), reads.
Trade-offs¶
- Server complexity: key storage + lookup + deduplication
- aging logic. Typically implemented once as a shared service framework module.
- Client discipline: client must remember to reuse keys across retries and not across operations. Easy to get wrong; lint rules and client-library wrappers help.
- Latency on first request: slight overhead for the key lookup on every write.
Related¶
- concepts/idempotent-operations / concepts/non-idempotent-operations — the properties that determine whether this pattern is needed.
- concepts/exponential-backoff-jitter — the retry- scheduling companion.
- patterns/retry-on-5xx-not-4xx — the retry-when policy this pattern makes safe.
- patterns/circuit-breaker — orthogonal resilience companion.
- systems/stripe-api — canonical public reference.
Seen in¶
- sources/2023-07-25-zalando-all-you-need-to-know-about-timeouts — canonical wiki home. Zalando house-style rule for making non-idempotent writes safely retryable.