Ensuring Idempotency

1. Understanding Idempotent Operations

MethodIdempotentWhy
GET, HEAD, OPTIONSYesNo state change
PUTYesSame body → same final state
DELETEYesState after = absent
POSTNo (default)Creates new each time
PATCHDependsMerge yes; relative ops no

2. Implementing Idempotent POST Requests

TechniqueHow
Idempotency-Key headerServer stores key+response; replay returns cached
Client-supplied UUID in bodyReject duplicates by ID constraint
Natural unique keyUse email, SKU as deduplicator

3. Using Idempotency-Key Header

Stripe-popularized convention; now a draft IETF RFC.

Example: Idempotency-Key

POST /payments HTTP/1.1
Idempotency-Key: 7f2e1a4b-9c3d-4e5f-8a1b-2c3d4e5f6a7b
Content-Type: application/json

{"amount": 100, "currency": "USD"}

# First call:  201 Created (charge processed)
# Retry same key: 201 Created (cached response, NO duplicate charge)

4. Storing Idempotency Keys

StorageStored Fields
KeyIdempotency key (string)
Request fingerprintHash of method+path+body
ResponseStatus, headers, body
Created/expiresTimestamps
Lock stateTo handle concurrent retries

5. Handling Duplicate Requests

ScenarioServer Response
Same key, same payloadReturn cached response
Same key, different payload422 (key reused for different request)
Same key, in-flightWait or 409 Conflict

6. Returning Cached Responses

Header to EchoPurpose
Original status codeSame as first call
Idempotent-Replayed: trueIndicates cached reply
Original body bytesByte-for-byte identical preferred

7. Implementing Idempotency for Payments

PracticeReason
Mandatory Idempotency-KeyPrevent double charges on retry
UUID v4 / ULIDUnique, unguessable
Per-user key namespaceAvoid cross-user collisions
Persist 24h+Cover long retry windows

8. Handling Idempotency Key Expiration

TTLUse Case
24 hoursStripe default
7 daysLong-running batch jobs
After expirationSame key allowed for new request

9. Validating Idempotency Key Format

RuleReason
Min 16 chars, max 255Prevent short collisions / abuse
UUID/ULID recommendedSufficient entropy
Reject empty/whitespaceAvoid accidental disabling

10. Documenting Idempotency Behavior

Doc FieldContent
Endpoints supporting keyList explicitly
Key formatUUID, length
TTLStorage duration
Conflict behavior422 on payload mismatch