What Is a Webhook? A Developer's Guide
A complete explanation of webhooks — how they work, how they differ from APIs and polling, and when you should use them.
WebhookGuide
March 21, 2026
If you have ever integrated with Stripe, GitHub, or Slack, you have used webhooks. But if someone asked you to explain exactly what a webhook is, how it differs from an API call, and why it exists, you might find yourself fumbling for the right words. This guide gives you the complete picture.
The Basic Concept
A webhook is an HTTP request that a server sends to your application when something happens. Instead of your code asking a service "has anything changed?" over and over again, the service tells you when something changes by sending an HTTP POST request to a URL you specify.
Think of it like the difference between checking your mailbox every five minutes versus having the postal carrier ring your doorbell when a package arrives. One is polling. The other is a webhook.
Here is what a typical webhook payload looks like. This is what Stripe sends when a payment succeeds:
{
"id": "evt_1R2x3Y4Z5a6b7c8d",
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_1R2x3Y4Z5a6b7c8d",
"amount": 2000,
"currency": "usd",
"status": "succeeded",
"customer": "cus_9a8b7c6d5e4f"
}
},
"created": 1711036800
}
Your application receives this payload at a URL you registered in advance — something like https://yourapp.com/webhooks/stripe. You parse the JSON, verify it is authentic, and take whatever action is appropriate: update a database record, send a confirmation email, provision an account.
How Webhooks Differ From APIs
APIs and webhooks are complementary, not competing. Here is how they differ:
APIs are pull-based. Your application makes a request to a server when it wants data. You decide when to call. You control the timing. The server responds synchronously.
Webhooks are push-based. The server makes a request to your application when something happens. The server decides when to call. You have no control over the timing. You must be ready to receive the request at any time.
In practice, most integrations use both. You use the API to create resources, query data, and perform actions. You use webhooks to get notified when things change asynchronously — when a payment completes, when a pull request is merged, when a user updates their profile.
The Polling Problem
Before webhooks, the only way to detect changes was polling: calling the API repeatedly on a timer to check for new data.
# Polling — wasteful and slow
import time
import requests
while True:
response = requests.get("https://api.example.com/orders?status=new")
new_orders = response.json()
for order in new_orders:
process_order(order)
time.sleep(30) # Check every 30 seconds
Polling has serious problems. It wastes bandwidth and API quota on requests that return nothing new. It introduces latency because you only detect changes at the polling interval. And it scales poorly — if you have 10,000 users each polling every 30 seconds, that is over 300 requests per second hitting the API for no reason.
Webhooks solve all three problems. There is zero wasted bandwidth because the server only sends a request when there is something to report. Latency drops to near zero because you are notified the moment the event occurs. And the load is proportional to actual activity, not to the number of subscribers.
The Lifecycle of a Webhook
Understanding the full lifecycle helps you build robust integrations. Here is what happens step by step:
1. Registration
You tell the provider where to send events. This usually happens through a dashboard or an API call:
curl -X POST https://api.example.com/webhooks \
-H "Authorization: Bearer sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/example",
"events": ["order.created", "order.fulfilled", "order.cancelled"]
}'
You specify the destination URL and which event types you care about. Most providers let you subscribe to specific events rather than receiving everything.
2. Event Occurs
Something happens on the provider's side — a customer makes a purchase, a build finishes, a file is uploaded. The provider creates an event record internally.
3. Delivery
The provider sends an HTTP POST request to your registered URL with the event data in the request body. The request typically includes:
- A JSON payload describing what happened
- A signature header so you can verify authenticity
- A unique event ID for deduplication
- A timestamp for ordering and replay protection
4. Your Response
Your endpoint receives the request, verifies the signature, processes the event, and returns a 200 OK status code. This tells the provider the delivery was successful.
If you return a non-2xx status code, or if the request times out, the provider will assume delivery failed and retry. Most providers retry with exponential backoff — waiting 1 minute, then 5 minutes, then 30 minutes, and so on — for up to 24 to 72 hours.
5. Acknowledgement vs. Processing
Here is a critical pattern that trips up many developers: acknowledge the webhook immediately, process it later. Your endpoint should validate the payload, enqueue it for processing, and return 200 within a few seconds. Do not perform slow operations — database writes, external API calls, email sends — inside the request handler.
# Good: acknowledge immediately, process async
@app.post("/webhooks/stripe")
def handle_stripe_webhook(request):
payload = request.body
signature = request.headers["Stripe-Signature"]
# Verify signature (fast)
event = verify_stripe_signature(payload, signature)
# Enqueue for async processing (fast)
task_queue.enqueue("process_stripe_event", event)
# Return immediately
return Response(status=200)
If your handler takes too long, the provider may time out the request and retry, leading to duplicate deliveries.
When to Use Webhooks
Webhooks are the right choice when:
- You need real-time notifications. Payment confirmations, deployment triggers, chat messages — anything where a 30-second delay is unacceptable.
- Events are infrequent and unpredictable. If a customer might make a purchase once a day or once a month, polling every 30 seconds wastes enormous resources.
- You are integrating with third-party services. Most SaaS platforms (Stripe, GitHub, Twilio, Shopify, SendGrid) offer webhooks as the primary integration mechanism.
- You are building event-driven architectures. Webhooks are a natural fit for systems where services communicate by publishing and subscribing to events.
Webhooks are not the right choice when:
- You need guaranteed ordering. Webhooks can arrive out of order, especially during retries. If strict ordering matters, you need additional logic.
- You need synchronous responses. Webhooks are fire-and-forget from the provider's perspective. If you need to send data back to the caller, use an API.
- The consumer cannot expose a public URL. Mobile apps, desktop applications, and serverless functions behind NAT cannot easily receive incoming HTTP requests. For these cases, consider server-sent events, WebSockets, or long polling.
Real-World Examples
Stripe sends webhooks for payment events. When a customer's card is charged, Stripe sends a charge.succeeded event to your endpoint. This is how you know to fulfill the order, even if the customer closed their browser before the redirect.
GitHub sends webhooks for repository events. When someone pushes code, opens a pull request, or creates an issue, GitHub notifies your CI/CD system, your deployment pipeline, or your project management tool.
Twilio sends webhooks for communication events. When an SMS is received or a phone call connects, Twilio sends the details to your application so you can respond dynamically.
Shopify sends webhooks for e-commerce events. When an order is placed, a product is updated, or a customer creates an account, Shopify pushes the event to your fulfillment system.
Common Pitfalls
Not verifying signatures. If you accept webhook payloads without checking the signature, anyone who discovers your endpoint URL can send fake events. Always verify.
Not handling retries. Providers will retry failed deliveries. If your handler is not idempotent — if processing the same event twice causes problems — you will have bugs in production.
Blocking on slow operations. If your webhook handler makes a slow database query or calls another API before returning, the provider may time out and retry. Acknowledge fast, process later.
Not logging raw payloads. When something goes wrong, you need the original payload to debug. Log it before you process it.
Hardcoding event structures. Webhook payloads change over time. Providers add new fields, deprecate old ones, and introduce new event types. Write defensive code that handles unexpected fields gracefully.
What to Learn Next
Now that you understand what webhooks are and how they work, the next steps are:
- Security — Learn how to verify webhook signatures using HMAC and protect against replay attacks.
- Testing — Set up local development tools like ngrok so you can receive webhooks on your machine.
- Reliability — Design your webhook handlers for idempotency, graceful retries, and failure recovery.
Webhooks are one of the most important integration patterns in modern software. Understanding them well will make you a better backend engineer, a faster integrator, and a more effective debugger when things go wrong at 2 AM.
Webhook Best Practices for Developers: Security, Reliability
Webhook best practices for developers to secure, verify, and reliably deliver events at scale—learn proven tactics to reduce failures and retries.
Webhook Testing Checklist: Complete QA Guide
Webhook testing checklist: validate payloads, signatures, retries, and security with this complete QA guide to launch reliable webhooks confidently.