By Anand Pai V
A timeout can hide a successful operation. Learn what idempotency protects, how a request key identifies one intention, and when repeating a request could create a duplicate.
Your application asks a server to create a document. The server saves it, but the response is lost. Your application sees a timeout and tries again. The server creates another document.
Both requests were valid. The missing information was whether the first operation had already happened. Before adding retries, decide how the server will recognize that both attempts represent one intention.
All requests below are conceptual examples. They require no account, credentials, or network calls.
A timeout leaves the outcome unknown
Consider this sequence:
- A client sends “create document.”
- The server creates document 731.
- The response disappears before reaching the client.
- The client cannot tell whether document 731 exists.
The timeout describes what the client observed. It does not prove that the server did nothing. AWS's discussion of retry design uses this uncertainty to explain why retrying creation operations can produce unwanted duplicates. AWS Builders' Library
Waiting longer between attempts can reduce traffic pressure. It does not, by itself, let the server distinguish a duplicate attempt from a new request.
Check the intended effect
HTTP calls an operation idempotent when repeating an identical request has the same intended server effect as performing it once. PUT, DELETE, and safe methods have this property in HTTP's defined semantics. The responses can differ. Servers can also log every attempt. HTTP Semantics, section 9.2.2
Imagine a service whose document resource contains only a title. Sending PUT /documents/731 with {"title":"Workshop notes"} sets that resource to the specified representation. Repeating it sets the same title again. By contrast, an operation that appends another paragraph on every call accumulates effects.
Similarly, a first DELETE /documents/731 might return 204 and a second 404. The intended final condition, absence of the document, can be the same even though the responses differ.
Read the actual endpoint contract. A method label cannot repair an implementation that violates its semantics. Concurrent edits also need separate protection: an idempotent replacement can still overwrite somebody else's newer change.
Give one intention one key
For a hypothetical creation endpoint that explicitly supports idempotency keys, the first attempt could be represented as:
POST /documents
Idempotency-Key: 48fca3a8-8bfe-4a38-bbcf-8eeb0f0c1c35
Content-Type: application/json
{"title":"Workshop notes"}
Suppose its documented contract stores the operation result with the key. When the response is lost, retry with the same key and the same payload. The server recognizes the existing operation and returns its recorded result instead of creating a second document.
Generate and persist the key before the initial attempt, then reuse it across that operation's retries. A freshly generated key on every attempt labels every attempt as a new intention. Conversely, if the user deliberately wants a second identical document, that is a new operation and needs its own key.
This distinction explains why deduplicating solely by request-body equality is risky: two intentional creations can have identical content. AWS describes caller-provided request identifiers and the need to coordinate recording the identifier with the actual mutation. A simple in-memory “seen keys” list is insufficient protection against crashes and concurrent requests. AWS idempotent API design
Read the limits before relying on them
An Idempotency-Key header works only when the service implements a corresponding contract. Check its retention period, parameter-mismatch behavior, concurrent-request handling, and retryable errors.
For a concrete provider example, Stripe API v1 records a request's status and body after execution begins, including 500 results. It rejects mismatched parameters for a retained key; keys can be removed after at least 24 hours. Reusing a pruned key creates a new request. These are provider-specific API v1 details, not universal HTTP guarantees. Stripe idempotent requests
If the outcome remains unknown and the service offers no duplicate protection, use its documented operation-status or reconciliation mechanism before creating again. An unchecked retry can compound the uncertainty.
Transfer exercise
Your document service retains idempotency keys for one hour. A create request times out. Ten seconds later, your retry code generates a new key and changes the title to “Workshop notes final.”
Identify both changes that prevent this from being a retry of the original operation. What should you do instead? What changes if you discover the timeout two days later?
Answer
The new key describes another operation, and the changed title changes the requested data. Within the documented retention window, retry the original payload with its original key. After establishing the original result, handle the title change as a separate update.
Two days later, the key may have expired. Reconcile the original outcome using the service's documented lookup mechanism before attempting another creation. Keep “unknown” as a real state until evidence resolves it.