On this page
What Is a Webhook?
A webhook is an HTTP callback your app receives when something happens elsewhere. Learn how they work, why they fail, and how to handle them safely.
Quick answer
- A webhook is an HTTP request a service sends to your URL when an event happens — a payment, a deploy, a new row in a database.
- The service registers your endpoint when you configure the integration, then pushes events to it in real time.
- Because the request arrives from a third party, you must verify its signature and make your handler idempotent.
How does a webhook work?
You give a service a URL — https://yourapp.com/webhooks/stripe — and tell it which events to send. When an event occurs, the service POSTs a JSON payload describing it to your URL. Your handler processes the event and returns 2xx to acknowledge. If it returns anything else or times out, the service retries with exponential backoff for a while, then drops the event. This push model replaces polling: instead of asking ‘did anything happen?’ every minute, you get told the moment it does.
Why should I use a webhook instead of polling an API?
Webhooks are event-driven: you react in seconds instead of on your polling interval, and you don’t burn API quota checking for changes that rarely happen. The tradeoff is complexity: now the third party calls you, so you need a publicly reachable endpoint, signature verification, and idempotent handling. Polling is simpler and works when events are rare and latency-tolerant. Many systems use both — webhooks for speed, a periodic poll as a safety net.
How do I handle webhooks safely?
Three rules. First, verify the signature: services like Stripe sign each request with a secret, and you must check it before trusting the payload — anyone can POST to a public URL. Second, make the handler idempotent: store the event ID you’ve already processed, because retries mean the same event can arrive twice. Third, return 2xx fast: do slow work in a background job and respond immediately, or the service’s retries will pile up.
# Stripe-style signature check with the raw body
import hmac, hashlib
def verify_signature(payload: bytes, sig_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig_header)Where this bites vibecoders
Webhooks are where AI-generated code meets the real world: the assistant writes a handler that parses the payload and updates the database, but skips signature verification and idempotency — so anyone can POST fake events, and a single retried event double-charges customers. The assistant also can’t test webhooks locally (the third party needs a public URL), which is why tunnel tools like ngrok exist. All three gaps are cheap to close once you know they’re there.
Where AI coding assistants get this wrong
- Handling the payload without verifying the signature, accepting events from anyone.
- Processing webhook work inline and slowly, so retries pile up and the queue backs out.
- No idempotency on the event ID, so retries double-apply side effects like charges.
- Not testing locally with a tunnel, so the first real event reveals a broken handler.
Checklist
- Verify the webhook signature with the raw request body before processing.
- Make handlers idempotent by tracking processed event IDs.
- Return 2xx quickly and move slow work to a background job.
- Test locally with a tunnel (ngrok) and a fake event from the provider.
FAQ
What happens if my webhook endpoint is down when the event fires?
The provider retries with backoff — typically a few times over a day or two — then drops the event. A dead letter queue or a periodic reconciliation poll catches what retries lose, which is why reliable integrations add both.
How do I test webhooks locally?
Use a tunnel like ngrok to expose your local server with a public URL, then point the provider’s webhook settings at that URL and trigger a test event. You can also replay past events from the provider’s dashboard once a handler exists.
Related topics
- How to Build a Background Job Queue
- What Is a Dead Letter Queue?
- How to Add Retry Logic to API Calls
- What Is Exponential Backoff?