basalt / webhooks/src / WebhookManager
Class: WebhookManager
Defined in: webhooks/src/index.ts:261
Register/list subscriptions and dispatch events to matching endpoints.
Constructors
Constructor
> new WebhookManager(store, deliverer, options?): WebhookManager
Defined in: webhooks/src/index.ts:268
Parameters
store
deliverer
options?
Returns
WebhookManager
Methods
dispatch()
> dispatch(event, data, scope?): Promise<DeliveryResult[]>
Defined in: webhooks/src/index.ts:464
Delivers to every endpoint subscribed to event, fail-closed on tenancy:
- inside a tenant context the delivery is FORCED to that tenant's endpoints plus tenant-agnostic ones (anti-widening);
- off the request path an explicit
tenantIddoes the same for that tenant; - with no tenant at all only tenant-agnostic endpoints are reached — never a tenant-bound one — unless
{ allTenants: true }asks for system fan-out. The result is re-filtered here, so a store that ignores the tenant argument can't widen delivery.
Parameters
event
string
data
unknown
scope?
string | WebhookDispatchOptions
Returns
Promise<DeliveryResult[]>
list()
> list(tenantId?, options?): Promise<WebhookEndpointView[]>
Defined in: webhooks/src/index.ts:447
Lists endpoints (secrets redacted). Anti-widening: a tenant in the ambient context always wins — a caller-supplied tenantId (which may carry client input) can never widen or switch the scope. With no context tenant, an explicit tenantId is honoured; no scope at all lists every endpoint, which with tenancy active requires { system: true }.
Parameters
tenantId?
string
options?
system?
boolean
Returns
Promise<WebhookEndpointView[]>
register()
> register(endpoint, options?): Promise<WebhookEndpoint>
Defined in: webhooks/src/index.ts:328
Registers an endpoint and returns it — including its signing secret, which is generated (whsec_…) when none is given for a tenant-bound endpoint (or when the deliverer has no default secret). Store it / show it to the customer now: list never returns secrets.
Bound to the ambient tenant when one is in context (a caller-supplied tenantId can't override it). With tenancy active and no tenant at all, pass an explicit tenantId, or { system: true } to deliberately create a global endpoint that receives every tenant's events.
The endpoint is validated before anything is stored: an unparseable URL, a scheme outside the deliverer's allowlist, a secret shorter than MIN_WEBHOOK_SECRET_LENGTH or an empty events list throws WebhookEndpointInvalidError; an id held by another scope throws WebhookEndpointIdInUseError.
Parameters
endpoint
Omit<WebhookEndpoint, "id"> & object
options?
system?
boolean
Returns
Promise<WebhookEndpoint>
rotateSecret()
> rotateSecret(id, options?): Promise<WebhookEndpoint>
Defined in: webhooks/src/index.ts:376
Rotates an endpoint's signing secret without breaking its receiver: the new secret becomes current, and for graceSeconds (default 24 h) every delivery is signed with BOTH — t=…,v1=<new>,v1=<old> — which verifySignature (and Stripe-style receivers) accept with either secret. The receiver switches to the new secret whenever it is ready; after the window only the new one signs.
Returns the endpoint with its new secret (hand it to the customer now — list() never returns secrets); the previous secret is not echoed back. Scoped like unregister: an endpoint outside the ambient (or given) tenant throws WebhookEndpointNotFoundError. An endpoint that signs with the plugin-wide default secret has no own secret to rotate — rotate the default in your configuration, or register() the endpoint with its own. graceSeconds: 0 is an immediate cut-over (e.g. after a leak).
Durable stores must persist previousSecret/previousSecretExpiresAt (the bundled SQLite and Prisma stores do; the Prisma schema needs the two columns — see its README).
Parameters
id
string
options?
WebhookRotateSecretOptions = {}
Returns
Promise<WebhookEndpoint>
unregister()
> unregister(id, options?): Promise<void>
Defined in: webhooks/src/index.ts:425
Removes an endpoint, scoped to the ambient tenant (or options.tenantId): a no-op for an endpoint another tenant owns. With tenancy active and no tenant, { system: true } is required.
Parameters
id
string
options?
system?
boolean
tenantId?
string
Returns
Promise<void>