Skip to content
TT
All writing

· 2 min read

Idempotency isn't a safeguard, it's the design

Every webhook arrives twice eventually. On money paths, that's not an edge case to handle later — it's the shape of the system.

ArchitectureEvent processingPayments

On an affiliate platform, a duplicated conversion event isn't a data quality issue. It's a partner being paid twice, with real money.

That framing changes when you deal with duplicates. Not "later, if it becomes a problem" — first, because it determines the shape of everything downstream.

The gap nobody designs for

The delivery guarantee you get from essentially every queue, webhook system and pixel is at-least-once. The guarantee your accounting requires is exactly-once.

That gap is not closed by infrastructure. It's closed by making your processing idempotent, so at-least-once delivery produces exactly-once effect.

async function processConversion(event: ConversionEvent) {
  // The idempotency key must come from the event's own identity,
  // never from the delivery attempt.
  const key = `conversion:${event.orderId}:${event.partnerId}`;

  const inserted = await db.conversions.insertIfAbsent({ key, ...event });
  if (!inserted) return; // Already counted. Not an error — the expected case.

  await payouts.credit(event.partnerId, event.commission);
}

The important detail is the key. It's derived from what the event is, not from when it arrived. A retry of the same conversion produces the same key; two genuinely different conversions never collide.

Where this gets missed

Three places, consistently:

  • The key includes a timestamp or attempt ID. Now every retry looks unique and you're back where you started.
  • The dedupe and the side effect aren't atomic. If you check for existence, then credit the payout, two concurrent deliveries both pass the check. The insert must be the guard.
  • Only the happy path is idempotent. Partial failures leave the record inserted but the payout uncredited, or vice versa.

The same rule, everywhere money moves

This isn't affiliate-specific. On Order Protection — a shipping-protection app across Shopify, Magento and BigCommerce — all three platforms deliver duplicate webhooks, and all three eventually deliver them late. The same idempotency discipline removed an entire class of duplicate-charge bug from a product that would otherwise have had it three times over.

If an event causes money to move, assume it arrives twice. Design for it once, at the start, and it stops being a category of bug you ever have to think about again.

Hiring for work like this?

I'm Thang Viet To — a senior backend & platform engineer with 9+ years on event pipelines, multi-tenant commerce systems and the platforms behind them.

Get in touch