UseBetter Webhooks
Add webhook delivery to your TypeScript app — payload signing, retries, and delivery logging included — without depending on an external service. UseBetter Webhooks runs inside your application as a library. You define event types, your customers register endpoints, and the library handles the rest. Everything is written directly to your own database. No external API calls, no per-message fees, no data leaving your infrastructure.
Works with any database your ORM adapter supports — Postgres, MySQL, SQLite, and more.
Concepts
Section titled “Concepts”- Event — a typed payload your application emits when something happens (e.g.,
invoice.paid,user.created). Events are defined in code with full TypeScript types. - Endpoint — a URL your customer registers to receive events. Each endpoint subscribes to one or more event types.
- Delivery attempt — a single HTTP POST to an endpoint. The library records the request, response status, headers, and body for every attempt.
- Signing secret — each endpoint gets a unique HMAC secret. The library signs every payload so receivers can verify it was not tampered with.
Why self-hosted?
Section titled “Why self-hosted?”Most webhook solutions are SaaS products: you push events to their API, they fan out delivery, and you pay per message. UseBetter Webhooks takes a different approach:
- Data ownership — webhook payloads, delivery logs, and endpoint configurations live in your database. Query them with any tool.
- No per-message pricing — deliver as many events as you want. Your only cost is compute and storage you already pay for.
- No vendor lock-in — the library is a dependency, not a platform. Swap it out without migrating data from a third-party service.
- No external calls — events are signed and dispatched from your own infrastructure. Payloads never pass through a middleman.
Architecture
Section titled “Architecture”| Layer | Package | Role |
|---|---|---|
| Core | @usebetterdev/webhook-core | Event model, adapter contract, signing, retry logic. Zero runtime deps. |
| ORM adapters | @usebetterdev/webhook-drizzle, @usebetterdev/webhook-prisma | Store endpoints, events, and delivery attempts. Own the database schema. |
| Framework adapters | @usebetterdev/webhook-hono, @usebetterdev/webhook-express, @usebetterdev/webhook-next | Routes for endpoint management and event ingestion. |
| Client library | @usebetterdev/webhook-client | React components for endpoint management UI. |
| CLI | @usebetterdev/webhook-cli | Migrations, health check, replay failed deliveries. |
| Umbrella | @usebetterdev/webhook | Single install, subpath exports for all adapters. |
You install the umbrella package (@usebetterdev/webhook) and import adapters via subpath exports like @usebetterdev/webhook/drizzle and @usebetterdev/webhook/hono.
When NOT to use this
Section titled “When NOT to use this”UseBetter Webhooks is designed for teams that want full control over their webhook infrastructure. If that does not describe you, consider the alternatives:
- You need webhooks in under five minutes and don’t want to manage infrastructure — a hosted service like Svix handles delivery, retries, and a management dashboard out of the box.
- You only send a handful of webhook types to a few endpoints — a simple HTTP client with basic retry logic may be all you need.
- You need global edge delivery with sub-100ms latency guarantees — a dedicated delivery network will outperform an in-process library.
If you want to own your data, avoid per-message costs, and keep webhook delivery inside your existing infrastructure, this library is built for that.