Push vs Poll · Registration · Delivery · Security · Retries · Real-world patterns
| Situation | Use |
|---|---|
| Payment completed (Stripe) | ✓ Webhook — event-driven |
| New PR opened (GitHub) | ✓ Webhook — immediate |
| Inbound SMS/call (Twilio) | ✓ Webhook — real-time |
| CI build finished | ✓ Webhook — push |
| API that doesn't support webhooks | Poll — no choice |
| Need historical backfill | Poll or REST query |
| High-frequency metrics | Poll or streaming |
| Pattern | Direction | When |
|---|---|---|
| Webhook | Server → your server (HTTP POST) | Event occurred |
| Polling | Your server → server (HTTP GET) | On schedule |
| WebSocket | Bidirectional persistent | Ongoing realtime |
| SSE | Server → browser (stream) | Server→browser push |
| Message queue | Async decoupled | Internal services |
The complete lifecycle: registration → event → delivery → acknowledgement. Every webhook follows this exact pattern.
Same problem — "tell me when a payment succeeds" — solved two completely different ways.
| Dimension | Polling | Webhook |
|---|---|---|
| Latency | Up to polling interval (5s–60s) | Milliseconds after event |
| Wasted requests | Most requests return "no change" | Zero — only fires on events |
| Server load | Constant — even when idle | Zero at rest |
| Complexity | Simple — just a loop | Need public URL, handle retries |
| Works behind firewall | Yes | No — must be publicly reachable |
| Can backfill history | Yes — query historical endpoint | No — missed events are gone |
| API rate limits | Burns quota constantly | Only on real events |
A webhook is just an HTTP POST with a JSON body and provider-specific headers. Understanding the structure helps you write robust handlers.
if seen(deliveryID) { return 200 } // already processedA public URL accepting POST requests is an attack surface. Without proper security, anyone can forge fake events. Three layers of defense.
HMAC-SHA256(secret, raw_body) and sends it in a header.What happens when your endpoint is down or returns an error? Every serious provider has a retry policy with exponential backoff. Understanding this is essential for reliable systems.
| Attempt | Delay | Total elapsed |
|---|---|---|
| 1st | Immediately | 0s |
| 2nd | ~5 min | 5m |
| 3rd | ~30 min | 35m |
| 4th | ~2 hours | 2h 35m |
| 5th | ~8 hours | ~11h |
| 6th–10th | Every 10h | up to 72h |
| Dead | After 72h | Event lost |
How webhooks are wired in production systems. Includes the fan-out queue pattern, local dev tunnels, and Cloudflare Workers as a webhook gateway.
pull_request event to your webhookhead.sha + branchpayment_intent.succeededGET /payments?created[gte]=yesterday → reconcile