Marcelo Pastorino, Software Developer

Idempotency

Safe to run more than once without changing the outcome beyond the first successful apply.

Idempotency means an operation can be repeated without changing the result beyond the first successful application. In distributed systems, this is the difference between a safe retry and a duplicated business action.

At-least-once delivery, HTTP timeouts, webhook retries, and job replays all create ambiguity. The caller may not know whether the receiver failed before doing the work, after doing the work, or after sending a response. Idempotent design lets the caller retry without double-charging, double-shipping, or creating duplicate records.

Common techniques

  • Idempotency keys. The client sends a stable key for a logical operation. The server stores the key and returns the original result for duplicate attempts.
  • Message IDs. Consumers record event IDs before applying side effects. The inbox pattern is the durable version.
  • Natural uniqueness. A database constraint on a business key prevents duplicate creation, for example one invoice per order.
  • Conditional writes. State changes include the expected current state, so stale or repeated commands become no-ops.
  • Upserts. The same input updates or creates the same row instead of appending another copy.

HTTP semantics

HTTP has method-level expectations, but business idempotency still depends on the implementation.

Method Idempotent by HTTP semantics Notes
GET Yes Reads should not mutate business state
PUT Yes Replaces a known resource with the same representation
DELETE Yes Deleting an already deleted resource should not create a new effect
POST No Use an idempotency key for retried creates or commands
PATCH Usually no Can be idempotent if the patch sets values instead of incrementing them

For APIs, the practical question is not only “can the server run this twice.” It is also “will the client see a stable outcome when it retries with the same key.”

Idempotency key flow

async function createCharge(request: ChargeRequest) {
  const existing = await db.idempotency.findUnique({
    where: { key: request.idempotencyKey },
  });

  if (existing) {
    return existing.response;
  }

  return db.transaction(async (tx) => {
    const charge = await tx.charge.create({
      data: {
        orderId: request.orderId,
        amount: request.amount,
      },
    });

    const response = { chargeId: charge.id, status: charge.status };

    await tx.idempotency.create({
      data: {
        key: request.idempotencyKey,
        response,
      },
    });

    return response;
  });
}

The key and the business write should commit together. If they are stored separately, a crash can leave the system in the exact ambiguity idempotency was meant to remove.

Design rules

  • Scope keys to the operation and actor. A key like abc123 should not collide across tenants or endpoints.
  • Store enough response data to make retries stable.
  • Set retention windows deliberately. Payment keys may need longer retention than analytics events.
  • Return conflicts when the same key is reused with different parameters.
  • Make downstream side effects idempotent as well. A safe API handler can still duplicate email, shipment, or ledger writes if those calls are not protected.

Trade-offs

Idempotency adds storage, indexes, and cleanup. It can also hide repeated user attempts if observability is weak. The cost is usually justified anywhere retries are automatic or money, inventory, access, or customer communication is involved.

See also

  • Webhook Reliability Patterns, Designing HTTP callbacks that survive retries, duplicates, and outages.
  • At-Least-Once Delivery, A message or event is delivered one or more times; duplicates are possible.
  • Webhook, An HTTP callback that notifies another system when an event occurs.
  • Outbox Pattern, Reliably publishing events by committing them in the same transaction as state changes.
  • Event-Driven Architecture, Decoupling producers and consumers through asynchronous messaging.
  • Inbox Pattern, Deduplicating inbound messages so consumer side effects run exactly once.

On Wikipedia