Marcelo Pastorino, Software Developer

Webhook Reliability Patterns

Designing HTTP callbacks that survive retries, duplicates, and outages.

Webhooks are HTTP callbacks: one system POSTs to another when an event occurs. They look simple but fail in predictable ways under retries and network partitions.

Patterns

  • Idempotency keys, deduplicate processing when the sender retries.
  • Fast 2xx, async work, acknowledge quickly, process in a queue.
  • Signature verification, validate HMAC or mTLS before acting on payload.
  • Dead-letter queues isolate poison messages without blocking the stream.
  • Exponential backoff on the receiver when downstream dependencies fail.
async function handleWebhook(req, res) {
  const eventId = req.headers["x-event-id"];
  if (await inbox.has(eventId)) {
    return res.status(200).send("ok");
  }

  res.status(200).send("ok");
  await queue.enqueue({ eventId, body: req.body });
}
async function handleWebhook(req: Request, res: Response) {
  const eventId = req.headers["x-event-id"] as string;
  if (await inbox.has(eventId)) {
    return res.status(200).send("ok");
  }

  res.status(200).send("ok");
  await queue.enqueue({ eventId, body: req.body });
}
public async Task<IActionResult> HandleWebhook(HttpRequest request)
{
    var eventId = request.Headers["X-Event-Id"].ToString();
    if (await _inbox.HasAsync(eventId))
        return Ok();

    Response.StatusCode = 200;
    await _queue.EnqueueAsync(new WebhookJob(eventId, await ReadBodyAsync(request)));
    return Ok();
}
async def handle_webhook(request):
    event_id = request.headers.get("x-event-id")
    if await inbox.has(event_id):
        return Response(status_code=200)

    # Fast ack, async processing
    await queue.enqueue({"event_id": event_id, "body": await request.body()})
    return Response(status_code=200)

Design for at-least-once delivery and idempotent handlers. The inbox pattern gives durable deduplication inside your database.

See also

  • Event-Driven Architecture, Decoupling producers and consumers through asynchronous messaging.
  • Outbox Pattern, Reliably publishing events by committing them in the same transaction as state changes.
  • Inbox Pattern, Deduplicating inbound messages so consumer side effects run exactly once.
  • Webhook, An HTTP callback that notifies another system when an event occurs.
  • Idempotency, Safe to run more than once without changing the outcome beyond the first successful apply.
  • At-Least-Once Delivery, A message or event is delivered one or more times; duplicates are possible.
  • Dead-Letter Queue, A holding queue for messages that cannot be processed, so failures do not block the main stream.

On Wikipedia

Marcelo Pastorino

Software Developer with 20+ years of experience creating business-focused web and cloud solutions.

All rights reserved © 2026