@playstack/webhooks
Verified inbound webhook receipt plus direct, durable, and Relaypath-backed outbound dispatch.
Pro. Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See package access.
@playstack/webhooks verifies exact request bytes, claims a provider event once, and inserts its normalized event into an application queue in the same transaction. It also exposes a small outbound dispatch contract for observational event routes. It does not provide event-store replay or provider business logic.
Install
After confirming preview access, install the package at your application's shared Playstack version:
npm install --save-exact @playstack/webhooks@0.1.0-beta.1Check the peer requirements below before choosing a runtime or provider.
Compose
import { createWebhooks, githubWebhookProvider, stripeWebhookProvider } from '@playstack/webhooks'
import { prismaWebhookPersistence } from '@playstack/webhooks/prisma'
const webhooks = createWebhooks({
providers: [stripeWebhookProvider({ secret, crypto, clock }), githubWebhookProvider({ secret: githubSecret, crypto, clock })],
persistence: prismaWebhookPersistence(prisma),
queue: applicationWebhookOutbox,
})
const result = await webhooks.receive(provider, {
headers: request.headers,
body: request.rawBody,
})Bridge contracts
| Contract | Boundary |
|---|---|
WebhookProvider | Verifies and parses exact raw bytes into a normalized provider event. |
WebhookPersistence | Transactionally claims the provider and event ID once. |
WebhookQueue | Inserts the verified event using the same transaction kind. |
WebhookHmacVerifier | Narrow SHA-256 verification contract compatible with @playstack/crypto. |
The built-in Stripe and GitHub providers handle signature verification; custom providers can retain the normalized payload and raw request as escape hatches. Unknown event types belong to application handlers and do not fail receipt.
@playstack/webhooks/prisma supplies a non-ejectable, security-critical receipt artifact and a structurally typed persistence adapter. Queue storage remains application-owned because it must share the application's transactional outbox.
See the Stripe and GitHub integration guides for provider-specific composition.
Send outbound webhooks
Use createOutboundWebhookHandler() to map an accepted Playstack event to one or more configured endpoints. Endpoint URLs, authorization headers, and signing keys stay in live application configuration rather than domain events or queued payloads.
| Dispatcher | Use |
|---|---|
@playstack/webhooks/direct | One-shot HTTPS delivery for development or small application hooks, with optional exact-body signing. |
@playstack/webhooks/durable | Self-hosted transactional enqueue, ordered endpoint leases, bounded retry, and paginated delivery history. |
@playstack/webhooks/relaypath | Relaypath-managed signing, retries, fan-out, endpoint health, and delivery history. |
The direct adapter rejects redirects and does not persist, retry, or record delivery. The durable adapter claims at most one ordered record per endpoint and classifies permanent versus retryable failures. Relaypath receives the source event ID as its idempotency key and only a managed endpoint ID—not the direct URL or secret headers.
An audience confirmation can therefore notify Slack or Discord by routing audiences.subscription.confirmed to a formatter and dispatcher without coupling the Audiences package to either service.
Customer-provided outbound URLs
HTTPS and redirect rejection alone are not an SSRF boundary. For Node public-endpoint delivery, inject createNodeWebhookFetch from @playstack/webhooks/node into createDirectWebhookDispatcher from @playstack/webhooks/direct. Configure the dispatcher's timeout; direct transport use must supply a deadline signal.
The transport validates all DNS answers, rejects non-public/special-use addresses, pins an approved address to the TLS lookup and preserves hostname/certificate verification. It resolves each attempt afresh, uses no shared agent/proxy, rejects redirects and defaults to port 443. Optional allowedHosts/allowedPorts never bypass address validation. Request bodies default to 1 MiB, response headers are capped at 16 KiB and response bodies are discarded.
Keep your infrastructure egress controls and deployment DNS/TLS tests in place; the transport's address checks complement them rather than replace them. Private-network or proxied delivery needs an explicitly owned transport.
API entry points and requirements
Reference snapshot: @playstack/webhooks@0.1.0-beta.1. Import only the entry point your runtime needs. Paths below are relative to the installed package; use Go to Definition in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.
| Public entry point | Declaration file |
|---|---|
@playstack/webhooks | ./dist/index.d.ts |
@playstack/webhooks/errors | ./dist/errors.d.ts |
@playstack/webhooks/prisma | ./dist/prisma.d.ts |
@playstack/webhooks/direct | ./dist/direct.d.ts |
@playstack/webhooks/node | ./dist/node.d.ts |
@playstack/webhooks/durable | ./dist/durable.d.ts |
@playstack/webhooks/relaypath | ./dist/relaypath.d.ts |
@playstack/webhooks/testing | ./dist/testing.d.ts |
@playstack/webhooks/package.json | No TypeScript declaration (asset or metadata export). |
Node.js engine requirement: >=20. This is not a claim that every entry point works in browsers or Workers.
Peer dependencies
This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.
For a complete first program, start with Getting started. For API lookup and partial-example conventions, see Reading the reference. Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.