Core
Compose endpoint management and PostgreSQL delivery.
The root entry point combines endpoint management and PostgreSQL delivery in one client.
import {
createWebhooks,
createDelivery,
generateEncryptionKey,
verifyWebhook,
WebhookError,
} from 'good-webhooks'
import type {
WebhookOptions,
Webhooks,
WebhookClient,
EndpointInput,
EndpointPatch,
WebhookErrorCode,
} from 'good-webhooks'The root also exports the shared types listed under other root exports. For the step-by-step setup, see installation.
createWebhooks
createWebhooks<const E extends EventDefinitions>(options: WebhookOptions<E>): Webhooks<E>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() }),
},
})WebhookOptions<E> accepts every delivery option except source, plus these fields:
| Field | Type | Meaning |
|---|---|---|
encryptionKey | string | Uint8Array | Required for the default PostgreSQL management provider. Base64-encoded 32 bytes or a 32-byte array. |
management | EndpointManagement | Optional existing provider. Its source supplies recipients and current endpoint state. |
Construction does not open connections, apply migrations, or start a worker.
- Without
management, the constructor creates standalone PostgreSQL management fromdatabase,schema, the configured event names,encryptionKey, andallowLocalhost.schemathen applies to both the management and delivery tables. - With
management, that provider keeps its own database, schema, encryption, and URL-validation configuration. Core options do not reconfigure it, andencryptionKeyis unused.
Invalid options throw INVALID_CONFIG: a missing event map, a malformed key, a database without query() and connect(), or any delivery option out of range.
Returned client
The client exposes publish, deliveries, deliverySettings, and worker from the delivery engine, plus these members:
| Member | Behavior |
|---|---|
endpoints.create(input) | Creates an endpoint and returns { endpoint, secret }. |
endpoints.list() | Returns all nondeleted endpoints in application scope. |
endpoints.get(id) | Returns one endpoint, including its deleted state. |
endpoints.update(id, patch) | Changes URL, description, or subscribed event names. |
endpoints.pause(id) | Records paused state. |
endpoints.resume(id) | Records active state after URL validation. |
endpoints.remove(id) | Records permanent deletion and clears stored signing secrets. |
endpoints.rotateSecret(id, { graceMs? }) | Returns a new signing secret and the endpoint. |
check() | Checks the default management provider, if constructed here, then delivery schema compatibility. |
forScope(scope) | Returns the same publication, history, settings, and endpoint methods bound to one named scope. |
endpoints
import { webhooks } from './webhooks.js'
const { endpoint, secret } = await webhooks.endpoints.create({
url: 'https://receiver.example/webhooks',
eventTypes: ['invoice.paid'],
})
await webhooks.endpoints.rotateSecret(endpoint.id, { graceMs: 60 * 60 * 1000 })Endpoint methods follow the management contract and limits, including its errors. EndpointInput<E> and EndpointPatch<E> restrict subscriptions to event names in E. removeScope and reencrypt are direct provider operations, not members of this client. The endpoints guide covers the lifecycle.
forScope
import { webhooks } from './webhooks.js'
const customer = webhooks.forScope({ type: 'organization', id: 'org_123' })
await customer.publish({ type: 'invoice.paid', data: { invoiceId: 'inv_1', amount: 4200 } })
await customer.endpoints.list()forScope validates and snapshots its scope, so later changes to the argument have no effect. An invalid scope throws INVALID_INPUT. It does not authorize a caller. See scopes.
The scoped client has no worker or check: those operate at installation level. WebhookClient<E> is its type.
check
import { webhooks } from './webhooks.js'
await webhooks.check() // Throws SCHEMA_MISMATCH if tables are missing or outdated.check() never applies migrations. When a provider is injected, it does not call that provider's optional health checks. The host stays responsible for checking its management storage.
generateEncryptionKey
generateEncryptionKey(): stringimport { generateEncryptionKey } from 'good-webhooks'
console.log(generateEncryptionKey()) // Store once in your secret manager.Returns 32 cryptographically random bytes encoded as base64. The key is the storage encryption key for standalone management signing secrets. It is distinct from the endpoint's whsec_ signing secret. Keep it outside the database.
Other root exports
| Export | Documented at |
|---|---|
createDelivery | Delivery entry point |
verifyWebhook | Receiver entry point |
WebhookError | Errors |
parseWebhook is available only from good-webhooks/verify.
The root also exports these types:
| Group | Types |
|---|---|
| Events | EventDefinitions, EventName, JsonValue, PublishInput, PublishOptions, PublishResult |
| Database | SqlClient, DatabaseClient, Database |
| Endpoints | Endpoint, EndpointStatus, EndpointWithSecret, EndpointInput, EndpointPatch |
| Delivery | Delivery, DeliveryDetail, DeliveryStatus, DeliveryQuery, Attempt, Page, WorkerResult, DeliveryOptions, DeliveryEngine |
| Composition | WebhookOptions, Webhooks, WebhookClient, EndpointManagement, EndpointSource, EndpointResolution, ManagementScope, Scope |
| Errors | WebhookErrorCode |