webhooks
Schema-first, type-safe webhook routing built on the standard Web API Request and Response primitives, with runtime-agnostic signature verification support.
webhooks is schema-first, type-safe webhook routing built on the standard Web API Request and Response primitives, with runtime-agnostic signature verification support.
Motivation
Webhook endpoints are usually built by hand, per provider, and this tends to repeat the same two mistakes. First, signature checks often use Node’s crypto module, which does not exist on Cloudflare Workers, Deno, or Bun’s edge runtimes — so the same code cannot run everywhere the webhook needs to be received.
Second, signatures are often compared with ===, which leaks timing information and opens a timing-attack risk that most teams do not know they have.
webhooks fixes both. It is built on the standard Request/Response objects, so the same router works on Bun, Deno, Cloudflare Workers, or any framework that accepts a Request.
Signature verification is opt-in through the verify option, and the built-in HMAC verifier uses the Web Crypto API (globalThis.crypto.subtle) with a constant-time comparison, so once you turn it on, you get the safe comparison without writing it yourself. Payloads are validated and typed straight from your Standard Schema — no any from JSON.parse, no manual casts.
Features
- Web API native —
handle(request: Request)returns aResponse, so the router plugs directly into Bun, Deno, Cloudflare Workers, Next.js route handlers, Hono, and any other fetch-compatible runtime. - Type-safe routing — handler payload types are inferred from the route schema.
- Standard Schema validation — bring Zod, Valibot, ArkType, or any compatible library.
- Signature verification — built-in HMAC verifier with constant-time comparison, or plug in your own
verifyfunction. - Lifecycle hooks — global
before,after, andonErrorhooks for cross-cutting behavior. - Runtime-agnostic — uses the Web Crypto API, not Node-specific APIs.
- Optional logging through
createWebhookRouter({ logger })fromlogger— omit it and there’s zero logging overhead. - Native OpenTelemetry — a
SERVERspan per delivery continuing the sender’s trace, plus a child span per handler dispatch, no-op until an SDK is registered. - Tree-shakeable — validation and hook-running internals are standalone functions; unused exports are dropped by any modern bundler.
Quick Start
import { ConsoleLogger } from "@zap-studio/logger";
import { createWebhookRouter } from "@zap-studio/webhooks";
import { z } from "zod";
const logger = new ConsoleLogger({ minLevel: "debug" });
const router = createWebhookRouter({ prefix: "/webhooks", logger });
router.register("/payments/succeeded", {
schema: z.object({ id: z.string(), amount: z.number().positive() }),
handler: ({ payload }) => {
// payload is inferred from schema
return Response.json({ processed: payload.id });
},
});
export default {
fetch: (request: Request) => router.handle(request),
};
Learn More
- Getting Started — install and build your first router step by step
- Web API Native — mounting the router in Bun, Deno, Cloudflare Workers, Next.js, and Hono
- Type-Safe Routing — how handler payload types are inferred from the route schema
- Standard Schema — using Zod, Valibot, ArkType, or any compatible library
- Verification — HMAC signature verification and the Web Crypto API
- Lifecycle Hooks —
before,after, andonErrorhooks - Logging — observe delivery attempts, dispatch, verification failures, and unmatched routes
- OpenTelemetry — native distributed tracing
Runtime Support
| Runtime | Minimum version |
|---|---|
| Node.js | 18.0.0 (router), 19.0.0 (verification helper) |
| Bun | 1.0.0 |
| Deno | 1.42 |
| Cloudflare Workers | Any current release |
| Browsers | Latest evergreen (Chrome, Edge, Firefox, Safari) |
The router only needs the standard Request/Response APIs, available globally since Node.js 18. The verification helper additionally needs globalThis.crypto.subtle, which is global by default from Node.js 19 (on Node.js 18, pass the --experimental-global-webcrypto flag). In browsers, Web Crypto requires a secure context (HTTPS). Deno 1.42 is the first release that can install packages from JSR (deno add jsr:@zap-studio/webhooks).