Contents

Backend Development › API Design · also in Queues & Async Processing

Designing Webhooks

Signatures, retries, ordering and idempotency for outgoing webhooks.

Also known as: webhook design, designing webhooks, outgoing webhooks

Designing webhooks is building the sender side: your system notifies customers’ endpoints when something happens (a payment succeeded, a record changed). Unlike a callback you control, the receiving endpoint is someone else’s — it can be slow, down, or buggy, and it must be treated as unreliable.

The design must answer several questions:

  • Signature and trust. Sign each delivery so the receiver can verify it came from you and wasn’t tampered with. Never send unauthenticated events.
  • Retries and backoff. Endpoints go down. Retry with exponential backoff and jitter, over a bounded window, then give up and mark the delivery failed (see jitter).
  • Idempotency and delivery. Retries mean duplicate deliveries; include an event ID so receivers can deduplicate (see idempotence).
  • Ordering. Events may arrive out of order (see message ordering); include a timestamp/sequence, and tell receivers not to assume order.
  • Delivery guarantees. Be explicit: typically at-least-once. Don’t promise exactly-once.
  • Payload and versioning. Define the event shape as a contract, include the event type and a version, and evolve it compatibly.
  • Failure visibility. Give senders a way to see failed deliveries and replay them.
event fires → sign payload → POST to subscriber → 2xx? done
                                            else retry with backoff → eventually failed

The classic mistakes:

  • No signature. Anyone can forge a webhook to a public endpoint; receivers can’t trust it. Always sign.
  • No retries. A single POST that fails loses the event. Retry with backoff.
  • Retries without idempotency. Retrying a non-idempotent receiver’s operation can double-apply. Provide event IDs so they can dedupe.
  • Assuming the receiver is fast. A slow endpoint shouldn’t block your system; deliver asynchronously via a queue.
  • Unbounded retry. Retrying forever can hammer a dead endpoint; cap attempts and surface failures.
  • Promising what you can’t. Claiming ordered, exactly-once delivery sets receivers up for subtle bugs. Be honest about at-least-once and no ordering.

How to build it: queue the event, sign it, deliver asynchronously with bounded retries, include an idempotency key and sequence, log delivery outcomes, and let senders replay. A well-designed webhook is a small reliable messaging system — see webhooks for the receiver side.