Skip to content
Zap Studio
webhooks
Esc
navigateopen⌘Jpreview
On this page

Lifecycle Hooks

Use before, after, and onError hooks for logging, metrics, and centralized error handling.

webhooks exposes global before, after, and onError hooks for cross-cutting behavior. Hooks are shared interception points around route execution — they keep handlers focused on business logic while logging, metrics, and error policy live in one place.

Add Global Hooks

Configure hooks once in createWebhookRouter — they apply to every route. Each of before and after accepts a single hook or an array; hooks run sequentially in registration order.

import { createWebhookRouter } from "@zap-studio/webhooks";

const router = createWebhookRouter({
  before: (ctx) => {
    console.log("incoming", ctx.path);
  },
  after: (_ctx, response) => {
    console.log("status", response.status);
  },
  onError: (error) => Response.json({ error: error.message }, { status: 500 }),
});

Every hook receives the webhook context — { request, rawBody, path } — where request is the incoming Web API Request (body already consumed by the router), rawBody holds the exact body bytes, and path is the matched route key.

Hook signatures (exported from webhooks):

  • BeforeHook(ctx: WebhookContext) => Promise<void> | void
  • AfterHook(ctx: WebhookContext, response: Response) => Promise<void> | void
  • ErrorHook(error: Error, ctx: WebhookContext) => Response | undefined, sync or async

Add Route-Level Hooks

Register before and after on a single route when one endpoint needs extra behavior beyond the global hooks.

import { z } from "zod";

router.register("/github/push", {
  schema: z.object({ ref: z.string() }),
  before: (ctx) => {
    console.log("route-before", ctx.path);
  },
  handler: () => Response.json("ok"),
  after: (_ctx, response) => {
    console.log("route-after", response.status);
  },
});

Understand Execution Order

For a successful request, the router runs:

  1. global before hooks
  2. route before hooks
  3. verify
  4. body parsing and schema validation
  5. handler
  6. route after hooks
  7. global after hooks

after hooks only run when the handler completes successfully. If any step throws, execution stops and error handling takes over.

Centralize Error Handling

When any hook, the verifier, or the handler throws, the router calls onError with the error and the webhook context. Return a Response to override the default response, or return undefined to fall back to the default 500 with { error: message }.

import { createWebhookRouter, VerificationError } from "@zap-studio/webhooks";

const router = createWebhookRouter({
  onError: (error, ctx) => {
    console.error("webhook failed", ctx.path, error.message);

    if (error instanceof VerificationError) {
      return Response.json({ error: "invalid signature" }, { status: 401 });
    }

    return undefined; // default 500 response
  },
});

onError gives you one place to standardize error shape and status codes, and to map provider or framework errors consistently. Validation failures do not reach onError — the router returns a 400 with the validation issues directly.

Last updated on September 15, 2026

Was this page helpful?