Developer tools · UUID generator
Idempotency Keys: Using a Client-Generated UUID to Make Retries Safe
· Why it matters
uuid cryptography browser-apis
A timeout on a payment request leaves you unsure whether it went through. Idempotency keys let you retry safely, and a CSPRNG-generated UUID is the natural key. This post explains the pattern end to end.
The timeout that might have charged the customer twice — the failure mode idempotency keys exist to fix
A timeout during a payment request creates genuine uncertainty for the client and the system. Your HTTP client gave up waiting for a response, but the payment server may have processed the transaction before the connection closed or timed out. If you retry the same request, you might charge the customer twice. If you do not retry, the payment never completes. The payment system fails into an unhappy middle ground: the customer's money might be gone, might arrive tomorrow, might be stuck in a processing queue, or might not have left the account at all. This ambiguity is unacceptable for financial systems.
How idempotency keys work — the server stores the first response under the key and replays it for repeats
Idempotency keys solve this problem elegantly by making retries safe and deterministic. The client generates a unique key for each intent—a payment, a transfer, a charge—and includes it in every request. The server processes the payment, caches the response under that key and stores both the key and result. If the same key arrives again within a retention window, the server replays the cached response without processing the payment again. The customer can retry confidently knowing that the exact same key will always yield the same result, no matter how many times it is sent. This pattern eliminates the ambiguity and makes retry logic safe.
Generate before the first attempt — why the key must exist before the request leaves and be reused verbatim on retry
The pattern is older than modern HTTP specifications but gained prominence in payments after widespread financial losses and customer complaints from duplicate charges. Every payment API and many web service APIs now support idempotency keys. A CSPRNG-generated UUID is the natural choice for the key because it is unguessable, unique without any coordination between clients and requires no server-side allocation or central authority. The client generates it before the first attempt, reuses it verbatim on every retry and receives the same response every time. No server-side state is needed to coordinate key generation.
Why a random UUID and not a counter or payload hash — uniqueness without coordination and no accidental reuse across intents
The key must exist before the request leaves the client because generating it on retry is too late to ensure idempotency. If the first request succeeded and charged the customer, generating a new key on retry would mask the problem and charge again. The client must commit to a key before the first attempt, store it in memory or persistent storage, and reuse that same key if a timeout or retry becomes necessary. For manual API testing, the ToolAcre generator produces keys you can paste into curl or a REST client, copy and reuse across multiple requests to test the idempotency behavior.
Scope and lifetime — keys per operation, per account, and how long the server should remember them
Why a UUID rather than a hash or sequential counter for idempotency keys? A hash of the request payload seems intuitive—identical payloads get identical hashes and thus identical keys. But hashes are weak for this use case because two almost-identical requests with different amounts, different recipients or different parameters produce completely different hashes and thus generate separate charges, which is correct but does not provide all the protection needed. A sequential counter requires coordination and distributed state: if two clients both generate counter-based keys on your infrastructure, their counters might collide. A UUID requires no central authority, is unguessable and is extremely unlikely to collide by chance across the entire Internet across all time.
Worked example — a retry sequence with the same key, showing what the client sends and what the server returns each time
The server-side implementation stores responses under keys and returns cached responses on repeats. The complexity is in deciding practical operational questions: the retention period how long to remember a key, the cache size how many keys to remember, locking how to prevent two concurrent requests with the same key from processing the payment twice and cleanup when to forget a key. These are storage and reliability questions outside the UUID generator's scope. The client's job is to generate a good key and reuse it on retries; the server's job is to implement the cache correctly and durably.
What this does not cover — the server-side storage and locking needed to implement the pattern, which is a separate design
A worked example shows a typical sequence in practice. A mobile app needs to transfer money to a friend using an API that supports idempotency. Before sending the request, the app generates a UUID using its local crypto library or fetches one from the ToolAcre generator for testing purposes: 3fa85f64-5717-4562-b3fc-2c963f66afa6. The app sends a POST request to /transfers with a JSON body and an HTTP header Idempotency-Key: 3fa85f64-5717-4562-b3fc-2c963f66afa6. The server processes the transfer, stores 3fa85f64-5717-4562-b3fc-2c963f66afa6 → {status: "success", transferId: "xfer-12345"} in its cache and returns a 200 response with the result.
Takeaway: one intent, one key — the ToolAcre generator gives you a CSPRNG-backed UUID to use as a key when testing an integration by hand
The network times out and the client does not see the response from the first attempt. The app retries the same request with the same idempotency key without generating a new UUID. The server recognizes the key in its cache, finds the cached response and immediately returns {status: "success", transferId: "xfer-12345"} without processing a new transfer and without charging the customer again. The operation is idempotent: retrying produces the same observable result every time. For a worked test, the ToolAcre generator can provide the key; generate a UUID, include it in the header, observe the response and resend with the same key to verify the server implements caching correctly.