HTTP method idempotency
- GET, HEAD, OPTIONS — safe (no side effects) and idempotent.
- PUT, DELETE — idempotent by definition. PUT sets state to X; calling it twice still leaves state as X.
- POST — not idempotent by default. Creating an order twice creates two orders.
- PATCH — depends on the operation. Incrementing a counter is not idempotent; setting a value is.
Idempotency-Key header
Clients generate a UUID for each logical operation and send it as a header. The server stores the response for that key. If the same key arrives again (retry), the stored response is returned instead of re-executing the operation.
POST /payments
Idempotency-Key: 01hwx45n2p-pay-4821
-- Server side (pseudocode)
key = request.headers["Idempotency-Key"]
if cache.exists(key):
return cache.get(key)
result = process_payment(request.body)
cache.set(key, result, ttl=24h)
return result
Storage
Store idempotency records in the same database as the operation, within the same transaction. This guarantees atomicity — no scenario where the operation succeeds but the idempotency record is not written.