Skip to content
Sadra Raadfar
Automation
Automation28 Jul 20267 min read

Idempotency is the feature you forgot to build

Retries are only safe when every write is keyed. Here is how I design for that from the start.

Most automation breaks in the same place. Not in the logic, not in the integration — in the second attempt. A webhook fires twice, a retry queue drains after an outage, someone resubmits a form, and suddenly the CRM has two of everything.

The fix is boring and it has to happen early: decide, for every write in the system, what makes that write the same write.

Start from the merge key

Before writing a single node, I list every destination the workflow touches and answer one question per destination: which field, or combination of fields, uniquely identifies this record in the real world?

  • Contacts: normalised email, lowercased and trimmed.
  • Companies: registered domain, not the display name.
  • Deals: contact plus source plus an open-state check.
  • Events: provider event id, stored so replays are cheap to detect.

Give the run an identity

Alongside record keys, each execution gets an idempotency key derived from stable input. That key travels with the payload and is checked before any side effect.

ts
const idempotencyKey = hash([
  payload.email.trim().toLowerCase(),
  payload.formId,
  bucket(payload.submittedAt, "10m"),
].join("|"));

if (await seen(idempotencyKey)) return { status: "duplicate" };

The time bucket matters. Without it a legitimate second enquiry three weeks later is swallowed as a duplicate. With it, accidental double submissions collapse and real repeat interest still gets through.

Order of operations

Diagram
  1. Receive
  2. Validate
  3. Dedupe check
  4. Side effects
  5. Record key
  6. Respond

Recording the key after the side effects, not before, means a crash mid-write leaves the run retryable. A crash after a successful write leaves at most one repeat, which the merge key absorbs.

Design the second run first. The first run is the easy one.

What this buys you

Once writes are keyed, retries stop being scary. You can be aggressive about backoff, replay a dead-letter queue without a review meeting, and let an integration fail loudly instead of half-succeeding quietly.

Keep reading