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 introduces latency equal to half your poll interval. 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.
How it works
- Register — you tell the third-party service: "POST to this URL when things happen." Usually done in a dashboard or via an API call.
- Event fires — something happens on their side (a payment completes, a pull request is merged). They build a JSON payload describing it.
- POST arrives — they send an HTTP POST to your URL. Your server must be publicly reachable, no localhost, unless you're using a tunnel like ngrok.
- Respond fast — return
200 OKimmediately, ideally within 5 seconds. If you don't, the sender assumes delivery failed and retries. - Process async — drop the event into a queue or background job. Do the actual work (send email, update DB) outside the HTTP response cycle.
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
Check your understanding
Why must a webhook handler respond before doing the real work?
Check your understanding