Thuta Learning
IntermediateWeb Developmentintermediate

Webhook Retries and Idempotent Handlers

What you'll walk away with

  • Explain the core ideas behind Webhook Retries and Idempotent Handlers
  • Read the diagram and trace how a request, response, or event flows through the system
  • Explain how this applies to a real API integration you might build

Build the mental model

Webhook delivery is not guaranteed to happen exactly once. If your endpoint is slow to respond or times out, most providers assume delivery failed and retry it, often several times.

This connects directly back to idempotency. An idempotent handler must produce the same end result no matter how many times the same event arrives — every duplicate delivery should be a safe no-op.

Receive and verify

Accept the request and verify its signature before reading anything from the body.

Check the event ID

Look up this event's unique ID in your processed-events store.

New: process and record

Do the real work, then record the ID so future duplicates are recognized.

Duplicate: acknowledge only

Skip the real work and return success immediately so the provider stops retrying.

Every event a provider sends carries a unique ID for exactly this reason; store it, check it, and duplicates stop being a threat.

text
IDEMPOTENT WEBHOOK HANDLING
---------------------------
-----
Receive request
      |
      v
Verify signature (reject if invalid)
      |
      v
Already processed this event ID?
      |
  +---+---+
  |       |
 yes      no
  |       |
  v       v
Acknowledge   Process + record ID
only          + acknowledge
(no reprocessing)

Connect it to a real scenario

The code below keeps a processedEvents set and a sideEffects log, standing in for a database table. handleWebhookEvent only touches the real side-effect logic when the ID is genuinely new.

Five deliveries arrive in sequence, including two repeats — exactly what a retry mechanism would produce. Compare the action field on each result against the sideEffects count.

Five requests arrive but only three distinct events trigger real processing. The two repeats get ACKNOWLEDGED_ONLY, meaning no order gets updated twice.

Try the working example

javascript
const processedEvents = new Set();
const sideEffects = [];

function handleWebhookEvent(event) {
  if (processedEvents.has(event.id)) {
    return { id: event.id, action: "ACKNOWLEDGED_ONLY", reason: "Already processed (duplicate delivery)" };
  }
  processedEvents.add(event.id);
  sideEffects.push(event.id);
  return { id: event.id, action: "PROCESSED", reason: "New event, side effect applied" };
}

const incomingDeliveries = [
  { id: "evt_001", type: "payment.completed" },
  { id: "evt_002", type: "payment.completed" },
  { id: "evt_001", type: "payment.completed" },
  { id: "evt_003", type: "payment.completed" },
  { id: "evt_002", type: "payment.completed" }
];

const results = incomingDeliveries.map(handleWebhookEvent);
console.log(results);
console.log("Total side effects applied:", sideEffects.length);
You should see
[
  { id: 'evt_001', action: 'PROCESSED', reason: 'New event, side effect applied' },
  { id: 'evt_002', action: 'PROCESSED', reason: 'New event, side effect applied' },
  { id: 'evt_001', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' },
  { id: 'evt_003', action: 'PROCESSED', reason: 'New event, side effect applied' },
  { id: 'evt_002', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' }
]
Total side effects applied: 3

5-minute try-it

Change the processedEvents store to also record a timestamp per event, and add a cleanup step that only keeps IDs from the last 24 hours.

One important caution

Processing the side effect before recording the event ID, which can double-process if the server crashes in between.

Relying only on your own request timeout to prevent duplicates instead of an explicit processed-event check keyed by event ID.

Idempotence — WikipediaAPI Integration & Webhooks

Easy traps

  • Processing the side effect before recording the event ID, which can double-process if the server crashes in between.
  • Relying only on your own request timeout to prevent duplicates instead of an explicit processed-event check keyed by event ID.
  • If you haven't taken the API Tutorial yet, it's worth finishing that first -- this course doesn't re-teach REST/HTTP/auth basics, it builds on top of them with webhooks, testing, reliability, and integration architecture.

Exercise

Change the processedEvents store to also record a timestamp per event, and add a cleanup step that only keeps IDs from the last 24 hours.

You'll know it worked when: [ { id: 'evt_001', action: 'PROCESSED', reason: 'New event, side effect applied' }, { id: 'evt_002', action: 'PROCESSED', reason: 'New event, side effect applied' }, { id: 'evt_001', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' }, { id: 'evt_003', action: 'PROCESSED', reason: 'New event, side effect applied' }, { id: 'evt_002', action: 'ACKNOWLEDGED_ONLY', reason: 'Already processed (duplicate delivery)' } ] Total side effects applied: 3

Webhook Retries and Idempotent Handlers | Thuta Learning