<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> WebHooks

WebHooks

WebHooks flip the request model: instead of your app asking a service "anything new?" over and over, the service calls you the instant an event happens. Imagine you're waiting for a package. You could call the delivery company every five minutes to ask "is it here yet?" or you could give them your phone number and let them call you when it arrives. Webhooks are the phone number.

What they are, when to use them

Before webhooks, apps used polling : make a GET request on a timer, check for changes, repeat. This wastes bandwidth, hammers rate limits, and adds delay since you only find out about an event on the next scheduled check. Webhooks eliminate all three problems.

A webhook is just an HTTP POST: the event source sends JSON to a URL you registered, and your server processes it. That's the whole mechanism. No special protocol, no persistent connection, no SDK required.

Polling App Service GET /updates 204 nothing new 200 new event! constant calls, mostly empty Webhook App Service event fires POST /webhook (event) 200 OK one call, the instant it happens
Polling asks 'anything new?' on a timer and mostly hears 'no'. A webhook stays silent until the event happens, then the service makes a single POST to you.

How it works

  1. Register: you tell the third-party service: "POST to this URL when things happen." Usually done in a dashboard or via an API call.
  2. Event fires: something happens on their side (a payment completes, a pull request is merged). They build a JSON payload describing it.
  3. POST arrives: they send an HTTP POST to your URL. Your server must be publicly reachable.
  4. Respond before the timeout: return a 200 OK before the sender's timeout, or it assumes delivery failed and retries. Where you do the work depends on how slow it is:
    • Quick work, do it inline: for something fast like a small DB write, just do it and then respond. You avoid the moving parts of a queue and the result is reflected immediately.
    • Slow work, queue it first: for something slow or unreliable (send an email, call another API, run a report), drop the event on a queue and return 200 OK right away. A background job does the real work outside the response, so a slow task can't blow the timeout and trigger a retry. The trade-off is added latency before the work actually finishes.

Senders typically add custom headers so you can route the event before parsing the body: x-webhook-event here, x-github-event or stripe-signature in the wild.

Common reasons you may need one

  • Payment events (charge succeeded, subscription cancelled) from Stripe and friends
  • CI/CD and repo events (push, PR merged) from GitHub
  • Any third-party integration where polling would be too slow or too expensive
  • Notifying your own services of events without coupling them together

Idempotency: handling duplicate deliveries

Networks are unreliable. If your server is slow to respond, the sender will retry, delivering the same event more than once. Without a guard, you might charge a card twice or send two welcome emails. Every webhook payload includes a unique event ID. Before processing, check whether you've seen that ID before. If yes, respond 200 and skip the work. This property, where running the same operation multiple times has the same effect as running it once, is called idempotency.

// Use an atomic insert to prevent race conditions
async function handleWebhook(event) {
  // If two duplicate webhooks hit at exactly the same time,
  // the database enforces the unique constraint and fails one.
  const inserted = await db.events.insertIfNotExists(event.id);
  if (!inserted) return; // another request beat us to it

  await processEvent(event); // safe to run exactly once
}

Check your understanding

Question 1 of 4

A slow handler keeps getting retried

Your handler sends a confirmation email inline before returning 200. The email provider is occasionally slow, and on those deliveries the sender gives up waiting and re-sends the same event. What's the right fix?