What about Svix?
Svix is a well-funded specialist, and they are good at delivery mechanics. We are not going to tell you that we beat them on retries and backoff.
You should choose based on something else: whether your customers’ payloads should pass through a third party at all, and whose product your customers see when a delivery fails.
The feature that looks like a week and costs a quarter
It usually starts with a sales blocker. An enterprise prospect asks if you support webhooks, and the answer decides the deal. Someone estimates a week of work: fire a POST when something happens.
Then the rest of the list shows up. You need delivery state per endpoint, exponential backoff, HMAC signing, and a secret your customers can rotate themselves. Endpoints that have been dead for a month need circuit-breaking. Some customers will want ordering guarantees, and most events need to fan out to several subscribers at once.
The part that rarely gets scoped is the customer-facing one. Your customers will want to see their own delivery history, and retry a failed delivery without filing a support ticket.
Added up, that is a quarter of engineering work on infrastructure that is not your product. Most teams reach for a vendor at this point, which puts a third party between you and your customers.
Where the delivery infrastructure lives
| Outbound webhook vendor | Codehooks |
|---|---|
| Your customers’ payloads pass through a third party | Payloads stay inside your own deployment |
| The delivery portal uses their UI and their branding | Delivery logs sit in your product, on your domain |
| One more subprocessor to disclose and contract with | Hosted in the EU (Amsterdam and Ireland) with no webhook vendor to add |
| New capabilities wait for their roadmap | You can change the code this afternoon |
| You rent the infrastructure, and the relationship with it | No queue, database or worker pool to provision, size or patch |
How do you send a webhook to a customer?
You deploy the template, then talk to it over HTTP, so the calling code stays in whatever language your app is already written in. Two calls cover most of the job: one registers a customer's endpoint, the other fires an event. Behind that second call the template fans the event out to every subscriber, signs each payload, retries the failures and records what happened.
- JavaScript
- Python
const BASE = 'https://YOUR-APP.api.codehooks.io/dev';
const headers = {
'content-type': 'application/json',
'x-apikey': process.env.CODEHOOKS_API_KEY
};
// Register a customer's endpoint, from your onboarding flow or admin UI
const res = await fetch(BASE + '/webhooks', {
method: 'POST',
headers,
body: JSON.stringify({
clientId: customer.id,
url: 'https://customer.example.com/hooks/orders',
events: ['order.created', 'order.shipped']
})
});
const { id, secret } = await res.json(); // show the secret to the customer once
// Later, when something happens in your app
await fetch(BASE + '/events/trigger/order.created', {
method: 'POST',
headers,
body: JSON.stringify({ orderId: order.id, total: order.total })
});
import os, requests
BASE = 'https://YOUR-APP.api.codehooks.io/dev'
HEADERS = {'x-apikey': os.environ['CODEHOOKS_API_KEY']}
# Register a customer's endpoint, from your onboarding flow or admin UI
res = requests.post(f'{BASE}/webhooks', headers=HEADERS, json={
'clientId': customer.id,
'url': 'https://customer.example.com/hooks/orders',
'events': ['order.created', 'order.shipped'],
})
secret = res.json()['secret'] # show the secret to the customer once
# Later, when something happens in your app
requests.post(f'{BASE}/events/trigger/order.created', headers=HEADERS, json={
'orderId': order.id,
'total': order.total,
})
Questions you are probably about to ask
Answered plainly, including the ones where a vendor is the better choice
Why not just use Svix?
Do my customers get a portal to manage their endpoints?
Can my customers replay a failed delivery themselves?
POST /webhooks/:id/retry clears the failure count and re-enables an endpoint that was switched off. Every event you fire is also stored in an events collection, so re-sending one is a query followed by a re-enqueue. What the template does not keep is a row per delivery attempt (add that to the delivery worker if your customers need the full history in your UI).What about secret rotation and signature verification?
Does running it myself mean I have to operate infrastructure?
How do I test the webhooks I am sending?
Where is the data stored?
Building the customer-facing part
The portal used to be the reason teams picked a vendor
Build-versus-buy for outbound webhooks has usually come down to the surface area nobody wants to build, and the customer-facing portal is most of that surface. This is where a coding agent changes the maths. The data model is already defined by the template, and CRUD screens over a defined model are exactly the kind of work agents do reliably.
A vendor portal stays theirs. It will never quite look like your product, and you can't adjust it next week when a customer asks for something.
To be clear about what you actually get: primitives and a working template, not a finished portal. Scope the UI work properly while you evaluate. We would rather you find this out now than after you have signed something.
Set up your coding agent (skill, MCP server and the coho prompt reference) →
Webhooks in both directions
This page covers the webhooks you send. Receiving them is the other half of the job.
Keep your customers’ webhooks inside your own product
Start from a template that already handles fan-out, signing, retries and delivery records, then change whatever you need to.
Your UI, your domain · Customer payloads stay in the EU