A webhook is an HTTP callback your service sends to a customer's URL when a relevant event happens.
A webhook is an outbound HTTP callback your service sends to a customer-provided URL when a subscribed event occurs. Webhooks are how modern SaaS systems notify each other in near-real time — Stripe fires a charge.succeeded webhook, Slack fires message events, GitHub fires push events. From the sender's side, webhooks look like 'just POST some JSON somewhere.' From integrators' experience, the difference between a well-designed webhook system and a poorly designed one is enormous: signature verification, retry semantics, event ordering, and payload stability all matter.
Every webhook must be cryptographically signed so the receiver can verify it actually came from you. Standard pattern: HMAC-SHA256 over the raw request body using a shared secret, sent as a header (Stripe-Signature, X-Hub-Signature-256). Include a timestamp in the signed payload to prevent replay attacks (reject if timestamp is more than 5 minutes old). Never rely on source IP for authentication — CDN and proxy topologies make it unreliable, and it doesn't prove payload integrity. Publish a well-documented verification code snippet for every language your integrators use.
Standard promise: at-least-once delivery. The receiver may see the same webhook multiple times and must handle it idempotently (include an event ID for dedup). Retry policy: exponential backoff over a fixed window (Stripe retries for 3 days; GitHub retries for 8 hours; both reasonable). A 2xx response means 'delivered'; any 4xx/5xx or timeout triggers retry. Failed webhooks after max retries go to a dead-letter state — provide a UI or API for integrators to inspect and replay failed events.
Do not promise ordered delivery unless you can genuinely guarantee it — most systems can't, because retries defeat naive ordering. Instead: include a timestamp on every event and design payloads so the receiver can reconcile without needing order. If order matters (state machine transitions), include the current state in the payload so the receiver can detect out-of-order events and refetch canonical state. Promising order and failing to deliver it produces subtle bugs in every integration.
Include enough context that the receiver doesn't need to immediately call back to your API — event type, event ID, timestamp, the affected resource ID, and enough of the resource state to act on. Full resource embedding is common (Stripe) and avoids callback storms; ID-only payloads (some GitHub events) minimize payload size but require every receiver to make a follow-up API call. For most APIs, embed the resource plus include an idempotency-friendly event_id.
What integrators actually want: (a) a testing tool — 'send me a fake event of this type to my endpoint.' (b) an event log — 'show me every webhook you sent me, with response codes.' (c) filtering — 'only send me the event types I care about.' (d) rotation — 'let me rotate signing secrets without downtime.' Systems that provide all four (Stripe, Svix as a service) become reference implementations; systems that provide none of them generate persistent support load and integration abandonment.
Investor directory · Fundraising library · Articles A–Z · Company funding database