Skip to main content

Outbound webhooks

Ship webhooks to your customers without shipping them to a vendor

Signing, retries, per-endpoint state and delivery logs, all running inside your own product on your own infrastructure.

Working template · Your UI, your domain · Customer payloads stay in the EU

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 vendorCodehooks
Your customers’ payloads pass through a third partyPayloads stay inside your own deployment
The delivery portal uses their UI and their brandingDelivery logs sit in your product, on your domain
One more subprocessor to disclose and contract withHosted in the EU (Amsterdam and Ireland) with no webhook vendor to add
New capabilities wait for their roadmapYou can change the code this afternoon
You rent the infrastructure, and the relationship with itNo 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.

your-app.js
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 })
});

Both calls send an API key because Codehooks routes are authenticated by default. The fan-out, the HMAC signing, the retry job and the per-endpoint delivery state all live in the template's index.js, so you can change any of them and redeploy.

Questions you are probably about to ask

Answered plainly, including the ones where a vendor is the better choice

Why not just use Svix?
In many cases you should. Svix is a well-funded specialist, and they are very good at delivery mechanics. We don't claim to beat them there. You would run this yourself for a different reason: your customers' payloads never pass through a third party, and the delivery history your customers look at stays inside your own product. If neither of those matters much to you, a specialist vendor is a perfectly sensible choice.
Do my customers get a portal to manage their endpoints?
Not a finished one, and it's worth knowing that before you scope the work. The template gives you the endpoint records, the delivery state on each one and a retry endpoint. You build the UI on top. That is less work than it sounds, because the data model already exists and the CRUD over it is well-trodden (a coding agent handles most of it in a day or two). The result looks like the rest of your product, because you built it there.
Can my customers replay a failed delivery themselves?
You can build that, and the template gets you most of the way. Each endpoint carries its delivery count, its last status and the error that came back, and 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?
Each endpoint holds its own signing secret, so rotating one is a write to a single record (no coordinated redeploy). Payloads are signed with HMAC SHA-256. The template also includes the verification snippet your customers paste into their own code.
Does running it myself mean I have to operate infrastructure?
No, and this is the part that used to make ownership expensive. There is no queue to provision, no database to size, no worker pool to scale and no server to patch. The queue, the database, the cron scheduler and the workers are already running behind your code, so "self-hosted" here means your code and your data rather than your servers. You own the delivery logic without owning an operations burden.
How do I test the webhooks I am sending?
Point a test endpoint at the webhook inspector and send deliveries to it. You get the exact payload, headers and signature your code produced, which is usually the fastest way to debug a signature your customer says will not verify. You can replay a captured delivery from there as well.
Where is the data stored?
In the EU, on servers in Amsterdam and Ireland. Delivery runs inside your own deployment, so there is no webhook vendor to add to your subprocessor list.

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.

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