Webhook Guides

Webhook Timeout Handling

Webhook timeouts are one of the easiest ways to create duplicate events. A provider sends a request, your receiver starts work, the response does not arrive quickly enough, and the provider retries. If the first attempt already changed state, the retry can repeat the same side effect.

The safer pattern is fast provider acknowledgement after durable capture, then controlled downstream delivery with retries, attempt history, and idempotency.

Webhook timeout and post-200 failure paths.
Timeout handling is about separating provider acknowledgement from downstream processing. The receiver still needs evidence for what happened after capture.

Timeout failure modes

  • The provider times out and retries while the first attempt is still running.
  • The receiver returns 200 after processing, but storage failed before the response.
  • The receiver calls a slow third-party API inside the provider request path.
  • The destination is overloaded and every provider retry adds more pressure.
  • A replay is triggered before the receiver is idempotent.

Safer receiver shape

Fast acknowledgement
async function webhookEndpoint(req, res) {
  const rawBody = await readRawBody(req);
  await verifySignature(req.headers, rawBody);

  const stored = await durableStore.insert({
    headers: req.headers,
    body: rawBody,
    receivedAt: new Date()
  });

  res.status(202).json({ accepted: true, requestId: stored.id });

  queue.enqueue({ requestId: stored.id });
}

What to move out of the timeout path

WorkProvider request path?Better place
Signature verificationYesBefore durable capture.
Durable request storageYesBefore response.
Billing, provisioning, fulfillmentNoDestination worker with idempotency.
Third-party API callsNoRetried async destination branch.

FastHook timeout pattern

Put FastHook at the provider edge when direct endpoints are too slow or too fragile. FastHook accepts and stores the provider request, then routes destination events through retryable attempts. You can inspect the timeout response, fix the receiver, retry one event, then replay a narrow window.

Related guides