To prevent a retry from charging a customer twice or repeating another mutation, give each logical operation one stable, high-entropy idempotency key and reuse it for every retry. The server must durably record that key with the request and its outcome, handle concurrent requests safely, and return the recorded result when it sees the same operation again. A key alone is not enough if the record and the business change can get out of sync.
What idempotency means—and what it does not
An operation is idempotent when repeating the same logical request produces the same server-side effect as performing it once. For example, setting an account preference to “dark mode” repeatedly should leave it dark; incrementing a balance by $10 repeatedly would add $10 each time unless the system prevents duplicates.
Idempotency concerns effects, not necessarily responses. Google Cloud’s HTTP guidance makes this distinction: a repeated request can have no additional effect even if the response is not identical to the first one. In practice, an API that stores and replays an operation’s first result can make retries easier for clients to handle, but that response behavior is part of the API’s contract.
HTTP methods and retry behavior
| Method or operation | Idempotent? | What that means for retries |
|---|---|---|
| GET | Usually, under HTTP semantics | Repeating a read should not create an additional server-side change. A response may still differ if the underlying data changes. |
| PUT | Usually, when it follows HTTP semantics | Repeating a request that sets a resource to the same intended state should not apply that change again. |
| DELETE | Usually, when it follows HTTP semantics | Repeating a deletion should not produce an additional deletion effect, though the server’s responses may differ. |
| POST | Not inherently | A create or other mutation may happen again unless the API supplies an application-level idempotency mechanism. |
| PATCH | Depends on the operation | Setting a field to a fixed value can be repeat-safe; applying a relative change, such as incrementing a number, is not automatically so. |
These are the usual semantics, not a guarantee that every endpoint implements them correctly. Check the API’s documentation before treating a request as safe to retry.
#1 Best Overall
Why a payment can be charged twice after a timeout
A timeout tells the caller that it did not receive a response in time; it does not prove that the server or payment processor failed to act. The processor may have accepted the charge and sent a response that was lost on the way back. If the customer or a worker retries without a stable operation identity, the retry can create a second charge.
With an idempotency key, the first request associates the key with the intended payment and its outcome. A retry carrying that same key can then be recognized as the same operation instead of a new payment. Stripe describes its API support for idempotency as a way to retry requests without accidentally performing the same operation twice. The exact behavior—including which results are replayed and how long keys remain available—depends on the provider’s contract.
Rank #2
- 78 pages (45 self-teaching + 33 quizzes/answers)
Implementing an idempotency key for a mutation
Use this pattern for an API or service that performs a retryable change such as creating a payment. The key identifies one logical operation, not one network attempt.
- Create a key once per intended operation. Use a high-entropy random identifier, commonly a UUID or equivalent. Do not use a predictable value such as a customer ID by itself.
- Keep the key stable across retries. Store it with the client’s operation state and resend it after a timeout or transient error. Do not generate a fresh key for each transport retry. If the user deliberately starts a different payment or changes the intended operation, that is a new logical operation and needs a new key.
- Persist the key and request identity. Record the key with the relevant request parameters, processing status, and eventual result in durable storage with a clearly defined scope. The scope might be an account, endpoint, or service; it must be narrow enough to avoid collisions between unrelated operations and consistent across the callers that need to deduplicate one operation.
- Claim the key and apply the mutation safely. Make the first claim concurrency-safe, using a transaction, lock, or optimistic concurrency control as appropriate. Concurrent requests with the same key must not both proceed as new operations.
- Check a reused key against the original request. Compare the incoming parameters with the recorded request. If they differ, reject the reuse rather than silently treating a different payment or mutation as the original one.
- Record and return the outcome. For a recognized duplicate, return the stored result according to the API contract. That can include replaying the original failure result if the provider specifies that behavior; do not assume every failure is eligible for a fresh attempt under the same key.
- Define key retention and expiry. Document how long a key and its result remain usable. Stripe documents that it may automatically remove keys once they are at least 24 hours old; after pruning, a retry can be treated as a new request. That is Stripe’s documented policy, not a universal retention period.
- Carry the key through downstream work. If the operation crosses a queue or calls another service, propagate an identity for that same logical operation so later components can deduplicate it too.
Protect the record and the business effect from partial failure
The key store, business mutation, and operation status must be coordinated. Otherwise, a crash can leave the system with a charge but no record of the key, or a recorded “complete” status without the corresponding business effect. A retry in either case may do the wrong thing.
Keep the state transition concurrency-safe
When two requests arrive with the same new key at nearly the same time, only one should claim it as new. The other should wait, receive a defined in-progress response, or read the completed result once available. Choose and document that behavior. A database uniqueness constraint or equivalent atomic claim can help enforce the rule, but the mutation itself must also participate in a safe transaction or recovery strategy.
Handle crashes around external services
A local database transaction cannot automatically make an external payment processor’s action atomic with your own records. Where the processor supports idempotency, send the same logical key downstream and save enough state to reconcile uncertain outcomes. If a crash occurs after the external service acts but before the local service records success, recovery should query or retry using the same identity rather than issue an untracked new operation.
Rank #4
Do not mark an operation complete merely because a request was sent. Track a status that distinguishes an accepted or in-progress action from a confirmed outcome, and define how uncertain cases are reconciled. The precise state model depends on the external API and business requirements.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Deduplicating queue messages and asynchronous actions
Queue delivery should be treated as potentially duplicated. A consumer can receive a message, perform its side effect, and then fail before acknowledging the message; the queue may deliver it again. Make the handler repeat-safe by attaching a stable operation or message identity, recording processed identities durably, and coordinating that record with the effect.
Recommended Free Tools
Best Value
For work that calls another service, propagate the identity rather than minting a new key at each hop. Otherwise the downstream service may see retries as unrelated actions. If one incoming operation intentionally fans out into several distinct mutations, give each child action its own identity while retaining a link to the parent operation for tracing and recovery.
Choosing or reviewing an idempotency design
When evaluating an API provider or your own implementation, check these behaviors explicitly:
- Key scope and entropy: What makes a key unique, and across which account, endpoint, or operation is it unique?
- Parameter mismatch: Does reusing a key with different request parameters fail clearly?
- Concurrent duplicates: What does a second request receive while the first is still processing?
- Result handling: Which success and failure outcomes are saved and replayed?
- Retention: How long does the key remain valid, and what happens after it expires or is pruned?
- Persistence and recovery: Can a crash between recording status and performing the effect create an untracked action?
- Propagation: Does the identity reach queues and downstream services that can repeat the action?
- Observability: Can operators distinguish a suppressed duplicate, a request still in progress, and a genuinely new operation?
These details determine the practical retry guarantee. “Supports idempotency” is not enough to establish how a particular timeout, concurrent request, changed payload, or expired key will behave.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →

