Stripe processes a payment. Your app needs to know when it succeeds. Two options:
Polling: your app calls Stripe every 30 seconds asking "did payment X succeed yet?" Wasteful at scale (millions of polls/sec for events that happen rarely). High latency to learn (up to 30s).
Webhook: Stripe POSTs to your URL (https://your-app.com/webhooks/stripe) the moment the payment completes. Sub-second notification, zero waste.
Webhooks are the standard async-callback pattern across the modern API ecosystem — Stripe, GitHub, Slack, Twilio, every webhook-capable SaaS. Building a reliable webhook system is harder than it looks.
Webhook Receiver — Signature First, Then Dedup, Then ACKMermaid
sequenceDiagram
participant S as Sender (e.g. Stripe)
participant R as Receiver (your app)
participant DB as Idempotency Table
participant Q as Worker Queue
S->>R: POST /webhooks/stripe headers: Stripe-Signature, body: {event}
R->>R: 1. Verify HMAC signature (raw body)
Note over R: Reject 401 if invalid
R->>R: 2. Verify timestamp within 5 min
Note over R: Reject 401 if stale (replay)
R->>DB: 3. INSERT event_id (idempotency check)
alt First time
DB-->>R: ok
R->>Q: enqueue for processing
R-->>S: 200 OK (within 1s)
else Already seen
DB-->>R: duplicate key
R-->>S: 200 OK (no-op)
end
Note over Q: Worker processes async — never block the ACK
3 days
Stripe webhook retry window
24 hr
GitHub webhook retry window
5 min
signature replay window
< 1 s
target ACK time
03
Receiver-side rules
Verify the signature first. Reject unsigned or stale events with 401. Anyone can hit your URL.
Persist before processing. Insert event_id into a "processed" table. Idempotency built in. Subsequent retries dedupe instantly.
Return 200 fast. Senders typically retry on non-2xx OR on timeout (often 10-30s). Long processing → ack quickly, push real work to a queue.
Don't crash on unknown event types. Senders add new types regularly. Log + ack 200 instead of 500.
Be ready for out-of-order delivery. Network = no order guarantees. Reorder by timestamp if the order matters.
04
Sender-side reliability
You're Stripe. Your customer's webhook endpoint returns 500 because their server is down. What do you do?
Retry with exponential backoff. Most webhook senders retry for 24-72 hours on failure. Backoff with jitter prevents thundering-herd recovery.
At-least-once delivery. Same event may arrive multiple times. Receiver dedupes via event_id (the contract).
Per-endpoint queues — a slow customer endpoint shouldn't block others. Bulkhead one queue per destination.
Dashboard for failures — show customers which events failed. Allow manual replay.
Eventual stop. After N days of failure, stop retrying and notify the integration owner. Endless retries waste compute.
05
Deep dive — the security checklist
Webhooks are public HTTP endpoints. Security failures are common and severe. The mandatory checklist:
HMAC signature. Sender computes HMAC(secret, timestamp + body). Receiver re-computes and compares. Use constant-time comparison to prevent timing attacks.
Timestamp window. Reject events older than 5 minutes. Prevents replay of old captured events.
HTTPS only. Plain HTTP exposes the signature + body to network attackers.
IP allowlist (optional). Senders publish their IP ranges (Stripe, GitHub do); receivers can firewall to those. Defense in depth.
Rotate secrets. If the shared secret leaks, generate a new one. Allow both old + new for a transition window.
Don't trust the URL itself. Don't put the secret in the URL — it ends up in logs.
Common bug
"We checked the signature later, after parsing the body" → attacker can send malformed JSON that crashes your parser before signature check. Always: read raw body, verify signature, then parse.
06
Real-world
Stripe webhooks
Industry gold standard
HMAC signed, retried for 3 days, dashboard with failed-event replay. Pioneered idempotency-key + signature pattern.
GitHub webhooks
Push, PR, issues, releases
HMAC-SHA256 signed via X-Hub-Signature-256. Per-repo configurable; per-org for org events.
Twilio
SMS, voice, status callbacks
Validates via X-Twilio-Signature. Retries delivery on non-2xx. Standard webhook pattern.
Svix / Hookdeck
Webhook-as-a-service
SaaS that handles delivery, retries, signing for you. Used by APIs that don't want to build the reliability layer.
07
Used in problems
Payment gateway publishes webhooks for every charge/refund/dispute. E-commerce uses webhooks for order-state notifications. Notification system itself is fundamentally a webhook delivery problem.