also: HTTP Callbacks · Event Notifications · Push Notifications
Webhooks are just HTTP POST callbacks with extra steps
Under the hood
- HTTP POST An HTTP request method that submits data to a server for processing. Wikipedia ↗ (opens in a new tab)
- callback function A function passed as an argument to be invoked later when an event occurs. Wikipedia ↗ (opens in a new tab)
What they say
Webhooks enable “event-driven architectures” and “real-time integrations.” They let services “react to events as they happen” and build “loosely coupled, reactive systems.” SaaS platforms describe their webhook support as a core integration capability.
What it actually is
When something happens, send an HTTP POST to a URL that someone registered ahead of time.1
That’s the entire concept. A webhook is a callback over HTTP. The “event-driven architecture” is: something changed → POST a JSON payload to a URL → the receiver handles it.
The pattern in pseudocode
// SENDER: when something happens, POST to registered URLs
async function emitEvent(event: string, data: any) {
const subscribers = await db.query(
"SELECT url FROM webhooks WHERE event = ?", [event]
);
for (const sub of subscribers) {
await fetch(sub.url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ event, data, timestamp: Date.now() }),
});
}
}
// When a payment succeeds:
await emitEvent("payment.succeeded", { id: "pay_123", amount: 4999 });
// RECEIVER: handle the incoming POST
app.post("/webhooks/stripe", (req, res) => {
const { event, data } = req.body;
if (event === "payment.succeeded") processPayment(data);
res.sendStatus(200);
});
The sender has a list of URLs. When an event fires, it POSTs to each URL. The receiver exposes an endpoint that handles the payload. That’s webhooks.2
The “extra steps”
- Signature verification — the sender signs the payload with a shared secret so the receiver can verify it’s authentic (HMAC, same as API auth)
- Retry logic — if the receiver returns a non-2xx status, retry with exponential backoff (standard retry pattern)
- Event filtering — letting subscribers choose which events they care about (subscription management)
- Idempotency — including a unique event ID so the receiver can deduplicate retried deliveries (idempotency keys)
- Delivery logs — tracking which webhooks succeeded or failed (an audit log)
What you already know
If you’ve ever passed a callback function to an event listener, you understand webhooks. The callback just happens over HTTP instead of in-process:
// In-process callback — you've written this
button.addEventListener("click", (event) => {
handleClick(event.data);
});
// Webhook — same pattern, over HTTP
stripe.webhooks.register("payment.succeeded", "https://myapp.com/hooks/stripe");
// Both: "when X happens, call this function/URL with the event data"
The mental model is identical: register a handler, wait for an event, process the payload. The transport changed from a function call to an HTTP request, which adds the retry/auth/idempotency concerns — but the pattern is a callback.3
Footnotes
-
Webhook — Wikipedia — the term was coined by Jeff Lindsay in 2007. The concept — “user-defined HTTP callbacks” — predates the name. It’s the inverse of polling: instead of the client asking “did anything change?” repeatedly, the server tells the client when something changes. ↩
-
Stripe Webhooks — the canonical modern implementation. Stripe’s webhook system is: register a URL, receive POST requests with JSON payloads, verify the signature, return 200. Every SaaS webhook system works essentially the same way. ↩
-
Observer pattern — Wikipedia — webhooks are the observer pattern over HTTP. The “subject” is the service emitting events; the “observers” are the registered URLs. Pub/sub, event emitters, and webhooks are all implementations of the same pattern at different scales. ↩