How it works
Start with the failure case: a client submits a payment, the server commits it, and the response disappears. The client cannot tell whether the payment happened. Retrying should recover the existing result rather than create another payment.
The client creates a unique key for the logical operation and sends it with every attempt, often in an Idempotency-Key header. A genuinely new operation gets a new key, even if its payload is identical. Scope keys to an authenticated tenant or account and an operation type so unrelated requests cannot collide.
The server stores the scoped key, a fingerprint of the validated request, processing status, and the result or a reference to it. Reusing the key with different parameters must be rejected. A completed duplicate returns the recorded outcome; an in-progress duplicate waits, polls, or receives a documented retryable response instead of starting another execution.
For effects inside one database, enforce a unique constraint on the scoped key and commit the business mutation and completed idempotency record in the same transaction. Concurrent attempts then cannot both commit the effect. If processing spans multiple transactions, use a durable state machine and recovery rules rather than leaving a permanent in-progress marker after a crash.
A local transaction cannot make an external charge or email atomic with your database. Persist the operation before dispatch, pass a stable idempotency key to the provider when supported, and reconcile uncertain outcomes. A timeout is not proof of failure, and an expired worker lease is not proof that its external action stopped.
Queue consumers use the same idea with a stable event ID: record that ID and apply the database effect in one transaction, then acknowledge the message. Redelivery finds the recorded ID and skips the effect. Keep deduplication records for the supported retry and replay window; once a record expires, an old request may execute again unless a durable business constraint prevents it.
A worked example
A payment succeeds, but the client sees a timeout
A customer submits a $75 payment with key checkout-842. The API durably creates a payment operation under the customer's account, then calls the payment provider using a stable key derived from that operation. The provider charges $75, but its response times out. The API leaves the operation pending rather than declaring failure. Two client retries with checkout-842 find the same operation and do not start independent charges. A recovery worker retries with the same provider key or queries the provider's authoritative status, then records the confirmed result. Later requests return that result. A request using checkout-842 for $90 is rejected because its payload differs.
Trade-offs to say out loud
- Extra durable writes and storage
- Request fingerprints, operation states, and stored outcomes add write load and retention costs to each protected operation.
- Retention bounds the guarantee
- Short expiration saves space but allows late retries or replays to execute again after their deduplication records disappear.
- Atomicity limits architectural choices
- Keeping deduplication and business changes in one transactional boundary is straightforward, while separate stores introduce crash windows.
- Concurrent retries need coordination
- A unique constraint prevents duplicate commits, but callers still need defined behavior while the original operation is running.
- External effects remain uncertain
- Without provider-side idempotency or an authoritative way to reconcile outcomes, you cannot generally guarantee both eventual execution and no duplicate external effect.
- Keys do not enforce every business rule
- Two different keys can still request the same forbidden action, so rules such as one claim per account need their own durable constraints.
When to reach for it
- Clients retry writes after timeouts, connection failures, or mobile reconnects.
- A payment, transfer, booking, or order must not be created twice for one logical request.
- Workers consume at-least-once messages or replay events that update counters, ledgers, or other durable state.
- Scheduled jobs or external integrations can be retried after a worker crashes with an uncertain outcome.
- The interviewer asks what happens when an operation succeeds but its acknowledgment is lost.
How to bring it up in the interview
- Name the logical operation and its identity first: for example, one checkout attempt or one scheduled job occurrence, not one HTTP attempt.
- State the invariant precisely: repeated attempts with the same scoped key must not create additional effects within the documented retention window.
- Describe the unique constraint and transaction boundary, then walk through two concurrent requests and a crash immediately before or after commit.
- Separate local database guarantees from external delivery guarantees; expect probes about provider timeouts, recovery ownership, and reconciliation.
- Explain key scope, payload mismatch handling, in-progress responses, and expiration before claiming that retries are safe.
Common mistakes
- Generating a new idempotency key for every retry, which makes each attempt look like a new operation.
- Using a check-then-write sequence without a unique constraint or atomic transaction, allowing concurrent duplicates through.
- Recording completion separately from the business mutation, leaving a crash window for duplicate effects or lost work.
- Assuming a cache lock, queue acknowledgment, or provider timeout proves that an external side effect happened exactly once.
- Treating matching payloads as the same intent, or assuming different keys should bypass business uniqueness rules.
Where it comes up
Interview questions where idempotency and idempotency keys is part of a strong answer, and why.
- Design a payment systemIdempotency keys and durable operation states prevent duplicate charges when clients retry after ambiguous failures.
- Design a wire transfer systemRetry-safe transfer identity must cover local ledger mutations while preserving uncertainty at the external bank boundary.
- Design a notification systemAt-least-once processing and uncertain provider responses require deduplication and an explicit policy for duplicate sends.
- Design a distributed message queue like RabbitMQVisibility timeouts and consumer crashes cause redelivery, so consumers need atomic deduplication of their business effects.
- Design a distributed job schedulerStable job-occurrence identities let workers retry without treating each delivery as a new intended side effect.
- Design a stock trading app like RobinhoodA retried order submission must recover the original order rather than place another trade when execution status is uncertain.
- Design an ad click aggregatorStable event identities and replay-safe updates prevent duplicate delivery from inflating counts and billing totals.
- Design Facebook likes at scaleRetry-safe like state and idempotent aggregation prevent repeated requests or replayed events from increasing totals incorrectly.
Questions people ask
What does idempotent mean for an API?
An API operation is idempotent when repeating it has the same intended effect as performing it once. PUT and DELETE have idempotent semantics in HTTP, while POST is not inherently idempotent but can be made retry-safe with an idempotency key. Idempotency does not require every attempt to return identical HTTP responses.
How do you generate and use an idempotency key?
Create a unique identifier, such as a UUID, before the first attempt and preserve it across retries of that logical operation. Send it in the API's documented field or header, commonly Idempotency-Key. Use a new key for a new intended operation, and have the server validate scope and reject reuse with different parameters.
Does an idempotency key guarantee exactly-once processing?
No: requests and messages may still be processed repeatedly, while deduplication prevents repeated committed effects within a defined boundary. A database transaction can protect a local mutation, but external effects require cooperation from the external system or reconciliation. Key expiration also limits how long duplicate attempts are recognized.