Skip to main content
Digital systems13 min read

Retries without duplicate effects: design idempotency as an API contract

A timeout does not reveal whether the server failed before or after the effect. Safely retrying POST or PATCH requires operation identity, request fingerprint, execution state, a retained result, and an expiry policy.

Updated
Nature
Architecture guide based on published RFCs, OpenAPI, and an expired Internet-Draft
Scope
APIs with material effects and clients subject to timeout/retry
Published and verified
4 August 2026
Limit
The contract must fit domain transactions, consistency, and risk
Two identical physical requests converge on a gate that produces one artifact and returns the replay result
A safe retry does not ignore the second request: it recognises the same operation and returns a consistent result without repeating the effect.AI-generated image under Velqory editorial direction.

In brief

HTTP defines idempotent methods by intended semantics, but that does not make every implementation or sequence automatically safe. Creation, payment, send, or change operations need domain deduplication. A key should be unique within principal, operation, and resource scope; the server binds it to a normalised payload, atomically wins the right to execute, and stores state and response. An equal replay receives a consistent result; the same key with a different payload is rejected; concurrent requests receive an explicit state. The client retries only documented conditions with backoff, jitter, and a limit.

  • HTTP idempotency describes the intended effect of repeated identical requests; it does not replace transactions, deduplication, or reconciliation.
  • The key should not be global: include identity/tenant, operation, resource, and content fingerprint.
  • Idempotency-Key draft -07 expired on 18 April 2026 and is not an RFC; use it as input, not a published standard.
  • Persisting only the key without state and result creates races, inconsistent responses, and duplication after failure.

Idempotent semantics and single execution are different promises

RFC 9110 defines PUT, DELETE, and safe methods as idempotent in terms of the client's intended effect. Logs, metrics, and other secondary effects may repeat. POST and PATCH are not idempotent by definition, but an application can offer retry-tolerant operation by contract.

The Idempotency-Key Internet-Draft contains useful ideas — uniqueness, fingerprint, responsibilities, and errors — but remains expired work in progress. Document behaviour in OpenAPI and do not claim compatibility with a standard that does not exist.

State of a retryable operation

  1. RECEIVE
    Validate scope and format

    Authenticate, authorise, and validate key, operation, resource, and payload.

  2. RESERVE
    Create atomic record

    Store composite key and fingerprint as in progress, or read the existing record.

  3. EXECUTE
    Produce one effect

    Use a transaction/outbox or domain-appropriate reconciliation.

  4. COMPLETE
    Persist result

    Store status, representation/reference, essential headers, and time.

  5. REPLAY
    Return or reject

    Same result for same fingerprint; key conflict for changed content.

  6. EXPIRE
    Apply explicit retention

    Reuse only after the contractual window and with replay risks understood.

Specify every state visible to the client

The happy path is easy. Document the response while the first request is running, after completion, after failure before effect, when effect is uncertain, when payload changes under the same key, and after expiry. Use Problem Details for machine-readable errors without exposing sensitive data.

Replay matrix

Stored stateNew requestResponseClient action
AbsentValid key and payloadAccept and reserveAwait result
In progressSame fingerprintConflict/in-progress + retry hintBounded backoff
CompletedSame fingerprintConsistent stored resultDo not repeat effect
Failed before effectSame fingerprintRepeatable error or controlled retryFollow contract
Uncertain resultSame fingerprintPending/reconciliationQuery state; do not recreate
AnyDifferent fingerprintKey conflictCreate new operation
ExpiredAnyDocumented behaviourDo not assume deduplication

Test where the network breaks the narrative

Inject failure before reservation, after reservation, before commit, after commit, and before the response. Repeat with two concurrent requests and two server instances. Count effects in the system of record, not only HTTP responses.

Choose retention from the longest retry window, asynchronous latency, reconciliation, and duplicate-operation risk. For payments or irreversible effects, prefer a durable status query and reference over guessing that the operation failed.

  • Do not treat the key as a secret or allow it across tenants.
  • Apply rate limits without deleting the deduplication record.
  • Retain a canonical hash/fingerprint without unnecessary sensitive payload.
  • Monitor replays, conflicts, stuck operations, expiries, and reconciliations.

Consulted on the stated update date.

  1. 01RFC 9110 — HTTP SemanticsIETF · SYS-IETF
  2. 02Idempotency-Key HTTP Header Field — Internet-Draft -07IETF HTTPAPI Working Group · SYS-IETF
  3. 03RFC 9457 — Problem Details for HTTP APIsIETF · SYS-IETF
  4. 04OpenAPI Specification 3.1.0OpenAPI Initiative · SYS-OPENAPI
  5. 05RFC 6585 — Additional HTTP Status CodesIETF · SYS-IETF
  6. 06RFC 9111 — HTTP CachingIETF · SYS-IETF