Installation
Add Good Webhooks to an existing application. Install, create a key and the tables, and connect.
Install the dependencies for your setup below. Better Auth endpoint management uses your existing database adapter. Built-in delivery and standalone PostgreSQL management require PostgreSQL 16 or 17.
Requirements: Node.js 24 or later. TypeScript consumers need version 5.9.3 or later with moduleResolution set to NodeNext or Bundler. Edge runtimes are not supported.
Install
npm install good-webhooks@beta pg zodnpm install --save-dev @types/pgThe beta tag installs the latest beta. Commit your lockfile. Payload schemas can use any Standard Schema library; these docs use Zod.
Create a storage key
Endpoint signing secrets are encrypted in your database with this key. Generate it once:
node -e 'import("good-webhooks").then(m => console.log(m.generateEncryptionKey()))'Store the output in your secret manager or .env as WEBHOOK_ENCRYPTION_KEY. Every process that sends or manages webhooks needs the same key.
Create the tables
Write the SQL to a migration file:
node -e 'import("good-webhooks/migrations").then(m => process.stdout.write(m.getPostgresMigration()))' > 001-webhooks.sqlApply it with the migration tool you already use, or with psql:
psql "$DATABASE_URL" --single-transaction -v ON_ERROR_STOP=1 -f 001-webhooks.sqlThe tables are prefixed webhook_ and go in public. To use another schema, see migrations.
Create the client
import { createWebhooks } from 'good-webhooks'
import { Pool } from 'pg'
import { z } from 'zod'
export const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export const webhooks = createWebhooks({
database: pool,
encryptionKey: process.env.WEBHOOK_ENCRYPTION_KEY!,
events: {
'invoice.paid': z.object({ invoiceId: z.string(), amount: z.number().int() }),
},
})Check the setup once at startup. It throws SCHEMA_MISMATCH if the tables are missing or outdated:
await webhooks.check()You're set up. Nothing is sent yet: that takes a published event and a running worker.
1. Publish events
Define your event catalog and publish from your app.
2. Run a worker
Start the process that sends and retries.
3. Manage endpoints
Register where events go.
4. Receive webhooks
Give your customers a verified receiver.
Entry points
Import only what a process needs. A receiver never loads database code.
| Import | Use it for |
|---|---|
good-webhooks | createWebhooks(): management and delivery together on PostgreSQL |
good-webhooks/delivery | createDelivery(): the sender alone, fed by any EndpointSource |
good-webhooks/management | The management contracts and createManagement() for custom storage |
good-webhooks/management/postgres | Standalone endpoint management without delivery tables |
good-webhooks/better-auth | The Better Auth server plugin and trusted management provider |
good-webhooks/better-auth/client | The Better Auth client plugin for browsers |
good-webhooks/verify | parseWebhook() and verifyWebhook() for receivers |
good-webhooks/migrations | getPostgresMigration() and the shipped SQL files |
The package ships ESM and also loads through CommonJS require() on Node.js 24. If you use the Better Auth plugin, enable skipLibCheck for its upstream declarations; your own types stay fully checked.