How to add webhooks to your API product
To add webhooks to your API product, define a catalog of event types. Then give customers an API and a UI to register endpoint URLs and choose events. Sign every payload with a per-endpoint secret, and deliver through a queue with retries. Finish by versioning payloads and exposing delivery logs so customers can debug without filing tickets.
How do I add webhooks to my API product?
Adding webhooks means building six pieces:
- An event catalog
- A subscription model
- Payload signing
- Asynchronous delivery with retries
- A versioning policy
- Customer-facing observability
Most teams get the first delivery working in a day. The rest takes months, because each piece has failure modes that only appear once real customers point real endpoints at you. The sections below cover each piece the way the major API providers document theirs. If you are new to the concept, start with what a webhook is.
Define your event catalog first
Your event types are a public contract, so design them before writing delivery code. Name them resource.action in the past tense: invoice.paid, user.deleted, subscription.renewed. That convention lets customers filter by prefix and makes new events predictable. The event naming conventions guide covers the edge cases.
Wrap every payload in the same envelope so consumers can route on type before parsing data:
{
"type": "invoice.paid",
"timestamp": "2025-03-04T18:22:10Z",
"data": {
"id": "inv_2Nf8",
"amount": 4900,
"currency": "usd",
"customer_id": "cus_91Kd"
}
}
Decide early whether you send full objects or only IDs that customers fetch back from your API. Full objects save a round trip. ID-only payloads avoid leaking stale or sensitive data. The tradeoffs are laid out in fat vs thin payloads, and designing webhook payloads goes further.
How do I let my SaaS customers subscribe to webhook events?
Let customers subscribe by exposing an endpoints resource in your API. A customer POSTs a URL and a list of event types. You validate the URL and generate a signing secret. You return the secret once. From then on, every matching event is delivered to that URL. Offer the same operations in your dashboard for non-developers.
POST /v1/webhook_endpoints
{
"url": "https://example.com/hooks/billing",
"event_types": ["invoice.paid", "invoice.payment_failed"],
"description": "Billing sync"
}
201 Created
{
"id": "ep_7Hq2",
"secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw",
"enabled": true
}
Each customer can have many webhook endpoints, and each endpoint gets its own secret. That way one leaked secret doesn’t compromise the others.
Validate URLs at registration and again at send time. Require HTTPS. Reject private, loopback, and link-local addresses so customers can’t use your delivery workers to probe your internal network, a classic SSRF vector.
If you run a multi-tenant product, scope endpoints per tenant. The multi-tenant webhook service guide covers isolation so one noisy tenant can’t delay everyone else. Svix ships an embeddable App Portal for this. Your customers manage endpoints, event filters, and secrets inside your UI, and you don’t have to build those screens.
Sign every payload
Customers need to prove a request came from you. Compute an HMAC-SHA256 over the message ID, a timestamp, and the raw body, using the endpoint’s secret. Send the result in headers. The Standard Webhooks spec defines webhook-id, webhook-timestamp, and webhook-signature headers. Adopting it means your customers can use existing verification libraries instead of writing their own.
Including the timestamp lets receivers reject old messages and blocks replay attacks. A timestamp tolerance of five minutes is common.
Support secret rotation by signing with both old and new secrets during a grace window. Customers can then rotate without downtime. More detail is in webhook security 101.
Deliver from a queue, with retries
Never send webhooks inline in the request that triggered the event. Write the event to a queue or an outbox table in the same transaction as the state change. Then let workers deliver it. Set a short timeout, commonly 15 seconds, and treat any non-2xx response or timeout as a failure.
Retry failures with exponential backoff over hours, not seconds. Customer outages are rarely brief. Svix’s default schedule, for example, retries after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours, and 10 hours.
After the final attempt, park the message in a dead letter queue. Disable endpoints that fail consistently, and notify the owner.
Retries mean delivery is at-least-once. Tell customers to deduplicate on the message ID. Your docs should say this plainly. The retry strategies guide covers jitter and circuit breakers.
Version events without breaking consumers
Adding a field is safe if your docs tell consumers to ignore unknown fields. Removing a field, renaming one, or changing its type is a breaking change.
You have two workable options for breaking changes:
- Ship a new event type such as
invoice.paid.v2and run both in parallel during a deprecation window. - Pin each endpoint to an API version at creation, the way Stripe does, and let customers upgrade per endpoint.
Pinning is friendlier but requires you to render every payload in every supported version. New event types are simpler to operate. Whichever you pick, publish a schema for each event type so customers can generate types and validate test payloads.
Give customers delivery logs and replay
The ticket you want to eliminate is “we never got the webhook.” Show customers, per endpoint, every attempt with its status code, response body, and latency. Let them resend a single message, or replay everything since a timestamp after an outage. Add a “send test event” button so they can check their handler before going live.
This is also where your own monitoring belongs: alert on failure rate per endpoint and on queue depth, not just on worker errors.
Build the delivery layer or use a service
Keep the parts that encode your business: the event catalog, payload contents, and when events fire. The delivery layer is generic plumbing. That covers queueing, signing, retries, rate limiting, endpoint management, logs, and replay, and it is where homegrown systems accumulate pager alerts.
If you have the engineering time and want full control, the webhook sender guide walks through the build. If webhooks are a feature your customers asked for rather than your product, evaluate webhook infrastructure against that build cost. Svix handles the delivery layer behind one API call per event, with Standard Webhooks signing and the embeddable portal included.
Start with the event catalog. It is the one piece you can’t outsource, and the one that is hardest to change after customers depend on it.