What is a webhook? How webhooks work and how to secure them
A webhook is a user-defined HTTP callback: one service sends an HTTP request to a URL you control when an event happens. Learn how they work, how they differ from polling and regular APIs, and how to verify signatures and handle retries safely.
On this page
- What a webhook actually is
- Webhooks vs polling vs a regular API
- How a webhook delivery works
- Verifying webhook signatures
- Handling retries and making your handler idempotent
- Webhooks vs other real-time options
- FAQ
- What is the difference between a webhook and an API?
- How do I test a webhook without waiting for a real event?
- What happens if my webhook endpoint is down?
- Is a webhook secure?
- Do I need a webhook or just an API?
- Verdict
A webhook is a user-defined HTTP callback: instead of you asking a service for updates, the service sends an HTTP request to a URL you provide whenever an event happens. When a payment succeeds on Stripe, when someone pushes code to a GitHub repository, or when a CI build finishes, the platform calls your endpoint and hands you the event. Webhooks are the standard way services push real-time notifications to your application without you polling for them.
What a webhook actually is
A webhook is a method for one web application to alter its behavior based on events from another. You register a URL (the endpoint) on the source service, choose which events you care about, and the service sends an HTTP request — usually a POST with a JSON payload — to that URL whenever one of those events fires.
Webhooks are built on HTTP, so they integrate into web services without adding new infrastructure. You only need a public endpoint that can receive a POST request. Because they reuse the plain web stack, they are the closest thing to a universal way for one service to notify another in real time.
The term was coined by Jeff Lindsay in 2007 from the general programming concept of a hook — a place where you can attach custom code to run when something happens.
Webhooks vs polling vs a regular API
The key idea is the direction of the request.
- Polling — your application repeatedly asks the service "anything new?" on a schedule. You control timing, but you usually get no real-time notification: the freshest data appears at the next poll, and a lot of requests return no new events.
- A regular API (question-answer) — your application calls the service when you decide, such as a REST call to
GET /users. The service answers on demand, but it will not reach out to you. - A webhook (event-driven) — the service calls your endpoint the moment an event happens. You get real-time delivery and only receive data when there is actually something to process.
None is universally better. Webhooks give you real-time push and avoid wasted polling, but they require a reachable, secured endpoint and careful handling of retries and failures. For a deeper look at the request side, see the API explained guide and the HTTP methods explained guide.
How a webhook delivery works
A typical webhook flow has three stages:
- Subscription — you create a webhook on the source service and give it the URL of your endpoint (for example
https://api.example.com/webhooks/stripe). You select the events you want and, for many services, a shared secret. - Event — something happens on the source side: a charge succeeds, a pull request opens, a deploy finishes.
- Delivery — the source makes an HTTP request to your endpoint. The request is almost always a POST with a JSON body describing the event, plus headers that identify the delivery. Your server returns a
2xxstatus to acknowledge it.
POST /webhooks/stripe HTTP/1.1
Host: api.example.com
Content-Type: application/json
Stripe-Signature: t=1724860000,v1=abcdef...
{
"id": "evt_1QxY2example",
"type": "payment_intent.succeeded",
"data": { "object": { "amount": 4999, "currency": "usd" } }
}
The type (or a header) tells you what happened, and the body carries the details. Because the webhook is a plain HTTP POST, the same endpoint can serve events from many different providers — a single route can fan out by event type to the right handler.
Verifying webhook signatures
Because a webhook is an unauthenticated-style inbound HTTP request, anyone who discovers your endpoint could POST fake events to it. To know the request is genuine, verify its signature — this is the single most important thing you do with an inbound webhook.
Most providers sign the request body with a shared secret using HMAC. GitHub sends X-Hub-Signature-256, Stripe sends Stripe-Signature, Slack sends X-Slack-Signature, Shopify sends X-Shopify-Hmac-Sha256. Your server computes the expected HMAC over the raw body with the secret and compares it to the header using a timing-safe comparison.
const https = require("node:https");
const crypto = require("node:crypto");
const secret = process.env.WEBHOOK_SECRET; // never hardcode secrets
function verify(payload, header) {
if (!header) return false;
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
// timing-safe compare — never plain ===
const a = Buffer.from(expected);
const b = Buffer.from(header);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
A few extra rules:
- Use HTTPS for your endpoint — never deliver or receive webhook payloads over plain HTTP.
- Check the timestamp — many providers include the delivery time so you can reject requests that are too old (a replay attack).
- Store secrets in environment variables or a secret manager, never in source code. For the surrounding pattern, see the environment variables guide.
- Log rejections — invalid signatures are often the first sign of probing or misconfiguration.
Handling retries and making your handler idempotent
Webhooks are delivered over HTTP, which can fail: a network blip, your server restarting, an endpoint that responds too slowly. Providers handle this by retrying. Stripe retries failed deliveries for up to three days, and Shopify retries repeatedly over a window. That means you may receive the same event more than once, in any order.
Two practices make this safe:
- Respond fast — return a
2xxwithin a few seconds, then process the payload asynchronously. If your handler is slow, the provider may time out and retry, which produces duplicates. - Make the handler idempotent — processing the same event twice must give the same result as processing it once. The standard approach is to check a deduplication key (the event
idor a delivery GUID header) and skip events you have already processed.
Retries also mean events can arrive out of order — a subscription updated event may arrive before the created event it depends on. Where order matters, rely on timestamps or sequence numbers instead of arrival order. For critical integrations, consider a dead-letter queue where persistently failing events go for manual review instead of being silently lost.
Webhooks vs other real-time options
Webhooks are not the only way to get real-time data:
- WebSockets keep an open, two-way connection for live streaming and interactive apps, but are not an event-delivery/retry system. See the WebSocket explained guide.
- Message queues and event streams (like Kafka) give durable, replayable, ordered delivery, at the cost of running more infrastructure.
- Webhooks are the simplest to consume — a plain HTTP POST — and pair well with a small retry/idempotency layer. Stripe, GitHub and Shopify all use this pattern.
A hybrid is common and pragmatic: use the webhook as a real-time notification and keep a polling or API endpoint as a recovery path to backfill any event you might have missed. For the request/response API side of the same story, see the REST API with Node.js and Express guide.
FAQ
What is the difference between a webhook and an API?
An API is a request/response interface your application calls on demand — you ask, the service answers. A webhook is the reverse: the service calls your endpoint when an event happens, delivering the data to you without you asking. Both use HTTP. Roughly: an API lets you pull, a webhook lets the service push.
How do I test a webhook without waiting for a real event?
Most providers include a test/demo panel that sends a sample or simulated event to your endpoint. For local development you can use a public tunnel to expose your local server (GitHub, Stripe and many others accept a tunnel URL), or use a tool that can send a crafted POST with the correct body, headers and signature so you can exercise your verification and handler logic.
What happens if my webhook endpoint is down?
The provider detects the failed delivery and retries according to its policy — for example Stripe retries for up to three days with exponential backoff. In the meantime you may miss events, which is why an idempotent handler plus an API or polling recovery path is recommended for anything where missed events matter.
Is a webhook secure?
A webhook is only as secure as your verification. Anyone who knows your endpoint URL could POST to it, so you must verify the signature of every request and reject anything that does not match, use HTTPS, check timestamps against replay, and keep the shared secret out of source code. With those in place, webhooks are a safe, standard way to receive events.
Do I need a webhook or just an API?
If your application actively initiates the interaction (fetch a user, submit a form) you need an API. If you want to react to events that happen on another service in real time (a payment, a deploy, a product update) without polling, a webhook is the right tool. Many integrations use both: webhooks for push notification, an API for backfilling and reconciliation.
Verdict
Webhooks are the standard building block for event-driven integrations, and they are simple to consume because they are plain HTTP POSTs. Use them when you want a service to push real-time events to you. To build them safely, verify every signature with a timing-safe HMAC comparison, use HTTPS, check timestamps, keep secrets out of code, return 2xx fast and process asynchronously, and make your handler idempotent so retries and out-of-order deliveries do not corrupt your state. When delivery is critical, pair the webhook with an API or queue recovery path so a missed event is never silently lost.
Sources
- Wikipedia — Webhook (en.wikipedia.org/wiki/Webhook
- CC BY-SA 4.0)
- Cloudflare Developers — Webhooks (developers.cloudflare.com/agents/communication-channels/webhooks/
- open-license)
- MDN — HTTP request methods (developer.mozilla.org/en-US/docs/Web/HTTP/Methods
- CC-BY-SA 2.5+)
- IETF RFC 9110 — HTTP Semantics (rfc-editor.org/rfc/rfc9110)
Share this guide
Related guides
- What is an API? A plain-language guide to how APIs work
What an API is, how REST APIs work over HTTP, the methods you'll actually meet — GET, POST, PUT, PATCH, DELETE — and how to read a response.
- REST API design best practices: resource naming, HTTP methods, and status codes
How to design a clean REST API: plural resource URLs, safe and idempotent HTTP methods, meaningful status codes, pagination, and error responses — with practical examples.
- What is unit testing? How to write, structure, and run unit tests
Unit testing is the practice of testing the smallest testable parts of a program — functions, methods, or classes — in isolation. Learn what a unit test is, the arrange-act-assert structure, test doubles and mocking, the testing pyramid, and best practices for writing good unit tests.