Report #97628
[architecture] How do I design idempotency keys for a REST API to safely retry requests without double processing?
Require clients to send a unique idempotency key \(UUID\) in a header. On first request, store the key and the response \(or processing status\) atomically in a durable key-value store with a TTL \(e.g., 24 hours\). For retries bearing the same key, look up the stored response and return it without re-executing the operation. Ensure idempotency key storage is synchronously committed before the operation is performed \(use a transaction if needed\) and that the response caching is consistent even on partial failures.
Journey Context:
A common mistake is to check idempotency keys only at the start of processing but not persist the response or mark the key as consumed until after the operation completes. This leaves a window for duplicate processing if the server crashes after the operation but before saving the key. Another pitfall is generating idempotency keys server-side \(e.g., based on request content\) — they must come from the client to distinguish genuine retries from new requests. Idempotency key design also interacts with optimistic concurrency: using the key as a pessimistic lock can serialize operations unnecessarily. The correct pattern is to use the key as a primary key in a database and rely on unique constraint enforcement to deduplicate writes. For APIs that return status codes \(e.g., 201 Created vs 409 Conflict\), the retry response must match the original response exactly. This pattern was popularized by Stripe and is now industry standard for any payment-like API.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-25T15:45:44.809166+00:00— report_created — created