Why It Matters
Distributed systems are unreliable by nature. Network failures, timeouts, and unexpected retries are not edge cases β they are inevitable. Without idempotency, any of the following can silently corrupt your data:Duplicate Bookings
A retry creates two reservations for the same user on the same slot.
Double Charges
A payment request retried after a timeout bills the customer twice.
Inconsistent State
Partial writes leave your database in an undefined, unrecoverable state.
Even a single duplicate transaction in a financial system can trigger compliance issues, failed audits, and customer disputes. Idempotency is non-negotiable.
How It Works
Every mutating request should include anIdempotency-Key header containing a unique, client-generated identifier:
First-time request
If no match is found, the request is processed normally and the result is persisted alongside the key.
Duplicate request (same payload)
If the key already exists and the payload matches, the original response is returned immediately β no reprocessing.
Behavior Reference
Implementation Guide
Generating keys
Always generate keys on the client side, before the request is sent. UUIDs (v4) are the standard choice.Persisting keys
Store the key before you send the request β not after. If your process crashes mid-flight, you need the key available for the retry.Retry with backoff
Combine idempotency keys with exponential backoff for safe, automatic retries:Always reuse the same key across all retry attempts for a single logical operation. Generating a new key on each retry defeats the purpose entirely.
Best Practices
Use UUIDs
Generate a cryptographically random UUID v4 for every new logical operation. Never derive keys from request data.
Persist before sending
Write the key to durable storage before making the request so retries after crashes use the original key.
Scope to one operation
One key = one operation. Never reuse a key for a different action, even if the payload looks similar.
Common Mistakes
| Mistake | Why Itβs Dangerous | Fix |
|---|---|---|
| Reusing keys across different requests | A cached response from operation A is returned for operation B | Generate a fresh UUID per operation |
| Not persisting keys before sending | A crash between send and store means retries use a new key, causing duplicates | Store the key first, then send |
Ignoring 422 conflict errors | Silent data corruption or unintended overwrites | Treat 422 as a hard stop β investigate before retrying |
| Using sequential IDs as keys | Predictable keys can collide across clients or sessions | Always use UUID v4 or a CSPRNG-generated value |
| Generating a new key per retry | Each retry is treated as a fresh request, defeating idempotency | One key per logical operation, reused on every retry attempt |
Key Expiry & TTL
Idempotency keys are not stored forever. Most systems enforce a TTL (typically 24 hours). After expiry, resubmitting the same key is treated as a brand-new request.Do not rely on idempotency keys as a long-term deduplication mechanism. For long-lived deduplication, implement application-level checks (e.g., unique constraints in your database).

