AlgoInsight

System Design

What Are Webhooks?

A webhook flips the API model around: instead of you constantly asking a service 'has anything happened?', it calls you the moment something does. Here's how that reversal works, and how to build one that's reliable and secure.

By Admin2026-06-1813 min readwebhooksapisystem-designintegrationevent-drivenbackend

Imagine your app needs to know the moment a customer's payment succeeds at a payment provider. One way: ask the provider over and over - "is it done yet? is it done yet?" - polling their API every few seconds. That's wasteful (most checks find nothing) and slow (you only learn about the payment at your next poll). There's a far better way: have the provider tell you the instant the payment completes, by calling your server directly. That reversal - the service calling you instead of you asking it - is a webhook.

Webhooks are how modern services notify each other about events in real time, and they're everywhere: payment confirmations, code-push notifications, form submissions, third-party integrations. This guide explains what a webhook is, how it works, how it differs from a regular API call, and - crucially - how to build a receiver that's reliable and secure, because the details matter.

What a webhook is

A webhook is a way for one server to notify another server when an event happens, by sending an HTTP request to a URL that the second server provided in advance. It's often described as a "reverse API call" or a "callback over HTTP": instead of you calling a service's API to request data, the service calls your URL to push data to you when something occurs.

The setup has two phases:

  1. Registration (once, ahead of time): you tell the service, "when event X happens, send a request to this URL of mine." You register your webhook endpoint with the provider.
  2. Notification (every time the event happens): when event X occurs, the service automatically sends an HTTP request (usually a POST) to your registered URL, with details about the event in the body. Your server receives it and reacts.

That's the whole idea: you provide a URL, and the service calls it whenever the relevant event happens. No polling, no asking - the information arrives the instant it's available.

sequenceDiagram
    participant Y as Your Server
    participant P as Provider (e.g. payment service)
    Y->>P: register "call https://myapp.com/hook when payment succeeds"
    Note over P: ...later, a payment succeeds...
    P->>Y: POST https://myapp.com/hook { event: "payment.succeeded", ... }
    Y->>P: 200 OK (acknowledged)
    Note over Y: Your server reacts to the event
This sequence diagram shows how a webhook works. Your server registers a URL with a provider, saying "call this URL when a payment succeeds." Later, when a payment actually succeeds, the provider sends an HTTP POST request to your registered URL with the event details. Your server responds with 200 OK to acknowledge receipt, and then reacts to the event. No polling is involved - the notification arrives the moment the event happens.

Why webhooks beat polling

The alternative to webhooks is polling - repeatedly asking a service "anything new?" Webhooks are dramatically better for event notification because:

This is why services overwhelmingly use webhooks (not polling) for event notifications. It's the difference between constantly checking your mailbox versus having the mail carrier ring your doorbell when something arrives.

What a webhook request looks like

When a webhook fires, the provider sends an HTTP request - typically a POST - to your URL, with a body (usually JSON) describing the event. Your server has an endpoint waiting to receive it. In rough terms, the receiving side looks like this:

// An endpoint on your server that receives webhook calls
app.post("/hook", (req, res) => {
  const event = req.body;            // the event details the provider sent

  if (event.type === "payment.succeeded") {
    fulfillOrder(event.data.orderId);   // react to the event
  }

  res.status(200).send("ok");        // acknowledge receipt quickly
});

Your endpoint reads the event, does whatever the event requires, and responds with a success status to acknowledge it. That acknowledgment matters - it tells the provider the webhook was received, so it doesn't think delivery failed.

The details that make webhooks reliable and secure

Webhooks sound simple - "just call my URL" - but building a robust receiver requires handling several realities. These details are what separate a production-grade integration from a fragile one.

Security: verify the caller. Your webhook URL is reachable over the internet, so anyone who discovers it could send fake events to it. To prevent this, providers typically sign each webhook with a shared secret - they include a signature computed from the payload and a secret only you and they know. Your server recomputes the signature and checks it matches before trusting the event. Verifying this signature is essential; without it, an attacker could send your server forged events (a fake "payment succeeded").

// Verify the webhook genuinely came from the provider
app.post("/hook", (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  const expected = computeSignature(req.rawBody, MY_SECRET);

  if (signature !== expected) {
    return res.status(401).send("invalid signature");  // reject forgeries
  }
  // ...trusted: process the event...
  res.status(200).send("ok");
});

Reliability: expect retries and duplicates. Networks fail. If your server is down or returns an error when a webhook fires, the provider will usually retry delivery - which means you might receive the same event more than once. Your handler must therefore be idempotent: processing the same event twice should have the same effect as processing it once (don't ship the order twice). A common approach is to record each event's unique ID and ignore any you've already handled.

Acknowledge fast. Providers expect a quick response (often a 200 status) to confirm receipt. If your handler does slow work before responding, the provider may time out and retry unnecessarily. The standard pattern is to acknowledge immediately and do any heavy processing afterward - often by dropping the event onto a queue and returning 200 right away.

Warning: The three details that trip people up: (1) not verifying the signature, leaving the endpoint open to forged events; (2) not handling duplicate deliveries, causing the same event to be processed twice; (3) doing slow work before acknowledging, causing timeouts and retries. A webhook receiver that ignores these works in testing and breaks in production. Verify, deduplicate, acknowledge fast.

A worked example: a payment from click to fulfillment

Let me trace a complete webhook flow. An online store uses a third-party payment provider and needs to ship orders only after payment truly succeeds.

First, the setup (once): the store registers a webhook URL with the provider - "when a payment event happens, POST the details to https://store.com/payment-hook."

Now a customer checks out. The store sends the payment to the provider, and the customer sees a "processing" screen. The key: the store does not poll the provider asking "done yet?" It waits for the callback.

The provider processes the payment (a few seconds, involving banks and fraud checks). When it succeeds, it sends a webhook: a POST to https://store.com/payment-hook with a body like { "event": "payment.succeeded", "orderId": "1234" }. The store's endpoint receives it, verifies the signature to confirm it genuinely came from the provider, checks it hasn't already processed this event (idempotency), and then fulfills order 1234 - triggering the shipment. It responds 200 immediately to acknowledge.

The order ships the instant payment cleared, with no polling anywhere. Had the payment failed, the provider would have fired a payment.failed webhook, and the store would handle that case (notify the customer, release the held inventory). One registered URL, and the store reacts in real time to whatever happens - the webhook pattern doing exactly what it's for.

Where webhooks fit among the alternatives

Seeing where webhooks sit among related tools clarifies when to use them:

The clean model: a webhook is for one server notifying another that a specific event occurred, efficiently and in real time.

A checklist for a robust receiver

The receiving end is where webhooks succeed or fail in production. A practical checklist:

Tick all of these and your integration is robust. Skip them and it'll appear to work in testing, then fail in subtle ways under real conditions.

The challenges webhooks bring

Webhooks are elegant but come with challenges worth naming:

None of these undermine webhooks - they remain the best tool for server-to-server event notification - but the recurring theme is that the receiver carries the responsibility: verify, deduplicate, acknowledge fast, and don't assume perfect ordering or delivery.

Where webhooks are used

Webhooks power countless integrations between services:

The common pattern is always the same: one service has an event, another service registered a URL, and the first calls the second the moment the event happens.

Webhook events and payloads

A practical detail about working with webhooks: providers usually offer many event types, and you subscribe to the ones you care about. A payment provider might emit dozens - payment.succeeded, payment.failed, refund.created, dispute.opened, and so on - and your endpoint inspects each incoming event's type to decide how to react (or to ignore types you don't handle). The payload (the request body) carries the event's details as structured data, typically JSON, including the event type, a unique event ID (used for idempotency), a timestamp, and the relevant object (the payment, the order, the user). Well-designed webhook systems keep these payloads consistent and documented, so receivers can reliably parse them. Part of integrating with a provider's webhooks is reading their documentation to learn which events exist, what each payload contains, and how their signatures work - the mechanics are universal, but the specific event names and payload shapes are defined by each provider.

Interview: Webhooks come up when a design involves services reacting to each other's events, especially third-party integrations. Proposing them over polling shows you understand event-driven efficiency: "Rather than polling the payment provider, I'll register a webhook so they notify us the instant a payment succeeds." The senior signal is naming the reliability details - "I'll verify the signature, make the handler idempotent since deliveries can be retried, and acknowledge fast by queuing the work." That awareness of the failure modes is what separates real experience from naming the concept.

The bottom line

A webhook is a way for one server to notify another when an event happens, by calling a URL the receiver registered in advance - a "reverse API call" that pushes data to you instead of you polling for it. It's far more efficient and real-time than polling, which is why services use webhooks for event notifications. But building a robust receiver means handling the realities: verify the signature so forged events are rejected, make the handler idempotent because deliveries can be retried and duplicated, and acknowledge quickly so the provider doesn't time out.

Get those details right and webhooks are the clean, efficient backbone of server-to-server event notification across the modern web. We cover event-driven integration and API design across System Design Fundamentals.


Go deeper

More articles