Release candidate — 1.0.0-rc.6
StitchAPI
GuidesResilience

Idempotency keys

Attach idempotency keys to writes so safe retries don't duplicate side effects.

Make retries on writes safe, so a replayed write settles once and never becomes a duplicate. Add idempotency to a write stitch and each logical call carries a stable Idempotency-Key header — when a retry replays the same write, the server sees the same key and dedupes the side effect instead of running it twice. Reach for it on POST/PUT where a duplicate would double-charge, double-ship, or double-create.

Example

import {  } from 'stitchapi';

// Default: a fresh random key per call, sent as 'Idempotency-Key'.
const  = ({
    : 'POST',
    : 'https://api.example.com',
    : '/charges',
    : true,
});

// Custom: derive a stable key from the input. `body` is `unknown`, so narrow it.
const  = ({
    : 'POST',
    : 'https://api.example.com',
    : '/orders',
    : {
        : () => `order-${(. as { : string }).}`,
    },
});

A single charge() call generates one key and reuses it across every retry of that call, so a network blip that triggers a retry settles a single charge.

Anti-pattern: don't lean on the default random key to dedupe two separate submissions — a double-clicked "Pay" fires two distinct charge() calls, each with a fresh key, so the server sees two writes and charges twice; derive a stable keyOf from something the duplicate submissions share (an order reference, or an id your client mints once per intent) so both map to one key. See Reference → Config types.

Options

header is the header name the key rides on, default 'Idempotency-Key'. Set it when the API expects a different name ('Idempotency-Token', a vendor-prefixed 'X-Idempotency-Key'). It renames the idempotency key — the value the server dedupes on — not a correlation or trace header like traceparent/X-Request-Id, which identify a request for logs and spans and belong to tracing, not dedupe.

keyOf builds the key from the call's StitchInput (params, query, body, headers, variables). The default is a random uuid generated once per logical call; a custom keyOf lets you derive a stable key from the request — for example an order reference — so the same logical write maps to the same key. body is typed unknown, so narrow or cast it before reading a property, as the createOrder example does.

Why it matters: idempotency pairs most naturally with retries — a retried write replays the same key, so the server recognizes the duplicate and skips the side effect, and safe retries don't double-charge. It isn't only for retries, though: the same key also covers a duplicate the stitch never issued (a proxy or the transport resending the request), and a derived keyOf dedupes two separate submissions with no retry at all.

Because the random default key mostly earns its keep alongside a retry, declaring it on a write with no retry logs a one-time construction nudge pointing you at retry (or a derived keyOf). It's a hint, not an error — set warn: false to silence it when the keyless-retry case (a proxy dedupe, say) is deliberate.

A custom keyOf derived from body must be stable for a given logical call and unique across distinct ones — colliding keys make the server dedupe two different writes into one.

See Reference → Config types for the full IdempotencyOptions shape.

See also

On this page