An idempotency key is a unique value that a client sends with a request to allow the server to detect and safely ignore duplicate submissions. If a payment request times out and the client retries, the server uses the idempotency key to recognise the duplicate and return the original response without charging the card twice.
UUIDs are the standard choice for idempotency keys because they can be generated client-side without coordination. The version matters for this use case.
Which UUID Version to Use
Use UUID v4 for idempotency keys — never UUID v7.
Idempotency keys must be:
- Unique — no two requests should share a key
- Unguessable — an attacker should not be able to predict or reconstruct a valid key
UUID v4 satisfies both requirements. Its 122 bits come entirely from a CSPRNG, making the key computationally infeasible to guess.
UUID v7 fails requirement 2. The high 48 bits encode the Unix timestamp in milliseconds. An attacker who knows approximately when a request was made can reduce the search space from 2¹²² to 2⁷⁴ — still large, but meaningfully weaker. For payment APIs and financial workflows, this tradeoff is not acceptable.
The Standard Pattern
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 9900,
"currency": "usd",
"customer_id": "cus_abc123"
}
The server workflow:
- Look up the idempotency key in the store
- If found and complete: return the stored response immediately (no re-processing)
- If found and in-flight: return
409 Conflict(another request is processing) - If not found: process the request, store the key + response, return the response
Generating Idempotency Keys
JavaScript:
const key = crypto.randomUUID(); // UUID v4, built-in to all modern runtimes
Python:
import uuid
key = str(uuid.uuid4())
Go:
import "github.com/google/uuid"
key := uuid.New().String() // uuid.New() generates v4
Java:
String key = UUID.randomUUID().toString(); // v4
Storage Strategies
Idempotency keys should be stored in a fast, atomic store:
- Redis with TTL:
SET idempotency:{key} {response_json} EX 86400 NX— theNXflag makes the set atomic, preventing race conditions when two identical requests arrive simultaneously - PostgreSQL: a table with a
uuidprimary key column and acreated_attimestamp for TTL-based cleanup - Distributed cache (Memcached, DynamoDB): suitable for stateless microservices where the key and response need to be accessible across service instances
The Stripe API documentation is the most cited reference implementation. Stripe stores keys for 24 hours and returns a 409 Conflict for concurrent requests with the same key.
Header Naming
No single standard defines the idempotency key header name, but these are the most common conventions:
| Header | Used by |
|---|---|
Idempotency-Key | Stripe, PayPal, most fintech APIs |
X-Idempotency-Key | Some internal APIs |
X-Request-ID | General request deduplication (slightly different semantics) |
The IETF draft for the Idempotency-Key header is working toward standardisation.
Related Resources
- Stripe idempotency documentation — the reference implementation
- IETF Idempotency-Key header draft
- Redis distributed lock patterns — for atomic key storage
- crypto.randomUUID() — MDN documentation
Frequently asked questions
Which UUID version should I use for idempotency keys?
Always use UUID v4 for idempotency keys. An idempotency key must be unguessable — UUID v7 embeds a millisecond timestamp in the high bits, which narrows the guessability space. UUID v4's 122 bits of CSPRNG randomness (from crypto.randomUUID() or equivalent) provides the unpredictability that idempotency keys require.
Are these UUIDs cryptographically secure?
The randomness is, yes — it comes from the Web Crypto API. That said, a UUID is an identifier, not a secret; don't use one as a password or an unguessable capability token on its own.
Should I use v4 or v7?
Use v7 for database primary keys (time-sortable, index-friendly) and v4 for anything where creation order could leak information, like tokens or share links.
Should I use UUID v4 or UUID v7 for idempotency keys?
Use UUID v4 for idempotency keys. An idempotency key must be unguessable — if an attacker can predict or reconstruct your key, they can replay requests as if they were legitimate. UUID v7 embeds a millisecond timestamp in the high bits, which narrows the search space for guessing. UUID v4's 122 bits of CSPRNG randomness provides the unpredictability that idempotency keys require.
How long should I store idempotency keys?
Store idempotency keys for at least 24 hours, matching the retry window you expose to clients. Stripe's idempotency documentation recommends a 24-hour window. Payment systems and financial APIs typically retain keys for 7–30 days to cover delayed retries and dispute workflows.