Programming

Idempotency Keys: Retry API Operations Without Duplicating Effects

tuyenpham
October 12, 2026 schedule 5 min read
idempotency keys: process overview

Idempotency keys help an API recognize retries of the same logical operation. A client that loses a response may not know whether the server already performed the action. Retrying with a stable key lets a supporting service avoid treating that uncertainty as a new operation. This is useful for orders, provisioning, and other effects that must not happen twice unintentionally. The header alone does nothing unless the server implements and documents the behavior. Design the request identity, persistence, concurrency rules, and failure recovery together.

idempotency keys: a practical implementation workflow

Keep one key for one intended operation

Generate an opaque, suitably unique key when an intended operation begins, then retain it across transport retries for that operation. A new action needs a new key. Creating a fresh key after each timeout removes the retry identity and can permit another effect. Conversely, reusing a key for unrelated orders can suppress a legitimate action or cause a mismatch error. Scope records to the authenticated caller and relevant operation so a key cannot expose another caller response. Document how a client recovers the key after a page reload or process restart.

idempotency keys: overview
Overview

Define payload and retention rules

Decide what happens when the same key arrives with different parameters. Comparing a canonical fingerprint of validated operation inputs can make accidental key reuse detectable. Do not assume every provider uses your exact canonicalization or expiry policy. Stripe documents its own request-result and key-retention behavior; those are provider-specific rules, not a universal HTTP guarantee. Your API should state its accepted key format, retention window, mismatch outcome, and retry guidance. Retention must cover the expected uncertainty period, while expired keys require a separate reconciliation decision.

Make concurrent claims atomic

Two retries can arrive before either finishes. An in-memory map on one process does not coordinate multiple instances and disappears on restart. Use durable storage with a uniqueness rule for the operation scope and key. Represent processing and completed outcomes explicitly, then define whether another caller waits, receives a pending response, or retries later. A check followed by an unprotected insert is not enough. For effects entirely within one database, align the key record and business write transaction so a crash cannot leave an ambiguous half-completed relationship.

Handle external effects as a distributed workflow

idempotency keys: checklist
Checklist

A database transaction cannot automatically include an independent payment or provisioning API. A crash can happen after the remote effect succeeds but before the local outcome is saved. Propagate a stable provider-supported idempotency identity where available and record enough information to reconcile the result. An outbox can support reliable intent delivery, but consumers and remote effects still need duplicate handling. Avoid advertising exactly-once execution as a property created by one header. Specify which effect is protected, for how long, and how uncertain states become known outcomes.

Classify failures before retrying

Validation failures, authentication failures, and network uncertainty are different conditions. Follow the target API contract rather than retrying every error indiscriminately. Bound retries, use an appropriate backoff strategy, and provide visibility when an operation remains unresolved. Store responses or result references only with suitable access control and data retention. Avoid logging full secret-bearing requests. Operational tools should let staff inspect an action identity and provider reference without initiating another business action merely to discover whether the previous attempt succeeded.

Code example

idempotency keys: workflow
Workflow

This browser-side example assumes your own API implements the key contract. Create and persist operationKey outside the function, reuse it only for retries of the same order, and reconcile ambiguous failures. This snippet does not implement the server-side guarantee.

export async function sendOrder(order, operationKey) {
  const response = await fetch("/api/orders", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Idempotency-Key": operationKey
    },
    body: JSON.stringify(order)
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

Worked example

A hypothetical client submits an order, but its connection closes before it receives the result. It retries using the same stored operation key and unchanged validated payload. The server recognizes the durable completed record and returns the documented outcome instead of creating another order. A new purchase gets a new key. A test also sends two simultaneous requests to different service instances. The desired assertion is one business record for one logical action, not merely two matching HTTP responses from a single happy-path process.

Verification checklist

  • Retain one key across retries of one action.
  • Reject or document mismatched payload reuse.
  • Coordinate claims with durable uniqueness constraints.
  • Test a crash after an external effect succeeds.
idempotency keys: mistakes
Mistakes

Practice task

Build a test API that creates a record and stores its operation key in a disposable database. Send identical requests concurrently and verify a single business effect. Reuse the key with a changed payload and assert the documented mismatch behavior. Simulate losing the response after commit, then restart the server and retry. Finally, add a fake external provider and inject failure between its success and local persistence. Write the recovery procedure before describing that workflow as reliable.

Sources and related reading

Consult Stripe idempotent request contract; Stripe API error handling for API contracts and implementation details. Continue with Api Development Basics and Database Transactions for related concepts.

Share Article: share

Discussion

Join the conversation

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Articles