---
title: webhooks
description: Schema-first, type-safe webhook routing built on the standard Web API Request and Response primitives, with runtime-agnostic signature verification support.
sidebar:
  label: Overview
type: package
package: "@zap-studio/webhooks"
---

`webhooks` is schema-first, type-safe webhook routing built on the standard Web API [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/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 a `Response`, 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 `verify` function.
- **Lifecycle hooks** — global `before`, `after`, and `onError` hooks for cross-cutting behavior.
- **Runtime-agnostic** — uses the Web Crypto API, not Node-specific APIs.
- **[Optional logging](/webhooks/logging)** through `createWebhookRouter({ logger })` from [`logger`](/logger) — omit it and there's zero logging overhead.
- **[Native OpenTelemetry](/webhooks/opentelemetry)** — a `SERVER` span 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

```ts
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](/webhooks/getting-started) — install and build your first router step by step
- [Web API Native](/webhooks/web-api-native) — mounting the router in Bun, Deno, Cloudflare Workers, Next.js, and Hono
- [Type-Safe Routing](/webhooks/type-safe-routing) — how handler payload types are inferred from the route schema
- [Standard Schema](/webhooks/standard-schema) — using Zod, Valibot, ArkType, or any compatible library
- [Verification](/webhooks/verification) — HMAC signature verification and the Web Crypto API
- [Lifecycle Hooks](/webhooks/lifecycle-hooks) — `before`, `after`, and `onError` hooks
- [Logging](/webhooks/logging) — observe delivery attempts, dispatch, verification failures, and unmatched routes
- [OpenTelemetry](/webhooks/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`).
