02Guides
Webhooks and callbacks
Payload, response and handler types for webhooks and callbacks.
Webhooks (OpenAPI 3.1 and later) and callbacks (3.0 and later) describe requests the API sends to you. swaggerz generates types for the receiving side, so the route handler you write is checked against the specification.
import type { AccountClosedWebhookHandler } from './generated/api';
export const onAccountClosed: AccountClosedWebhookHandler = async ({ body, headers }) => { if (headers['x-signature'] === '') return { received: false }; console.log('closed', body.id); return { received: true };};For every webhook, webhooks/<webhook>.ts exports:
| Type | What it describes |
|---|---|
<Name>WebhookPayload |
The request body the API sends |
<Name>WebhookRequest |
body, and the declared headers and query parameters |
<Name>WebhookResponse |
The body your handler sends back |
<Name>WebhookHandler |
(request) => response, sync or async |
Header names are lowercase, as Node.js and fetch expose them. Callbacks get the same types, named after their operation and callback: CreateAccountAccountEventCallbackHandler in callbacks/create-account-account-event.ts. Everything is exported from the main barrel.
Wiring a handler
Section titled “Wiring a handler”No server code is generated: the types fit any framework. Turn the incoming request into the handler’s input and send back what it returns, for example with the web-standard Request and Response that Node.js, Deno, Bun and edge runtimes share:
import type { AccountClosedWebhookHandler, AccountClosedWebhookRequest } from './generated/api';
export function route(handler: AccountClosedWebhookHandler) { return async (request: Request): Promise<Response> => { const input: AccountClosedWebhookRequest = { body: (await request.json()) as AccountClosedWebhookRequest['body'], headers: { 'x-signature': request.headers.get('x-signature') ?? '' }, }; return Response.json(await handler(input)); };}The cast is the trust boundary: the types say what the API promises, not what arrived. With features.validation, parse the body with its schema (AccountSchema.parse(...)) instead.
The payload comes from the API, so it is typed like a response: readOnly properties are kept and writeOnly ones left out. Your handler’s response goes back to the API, so it is typed like a request body (the …Input variant, see read and write models). output.webhooks: false turns the types off.
Limitations
Section titled “Limitations”- Types only: no routing, signature verification or server adapters are generated.
- The generated types describe requests; nothing checks at runtime that a request really came from the API.
What's generated
openapi: 3.1.0info: title: Accounts version: '1.0'paths: {}webhooks: accountClosed: post: summary: Sent when an account is closed. parameters: - name: X-Signature in: header required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Account' responses: '200': description: Received. content: application/json: schema: type: object properties: received: type: booleancomponents: schemas: Account: type: object required: [id] properties: id: type: stringwebhooks/account-closed.ts
/* eslint-disable */// @generated by swaggerz. Do not edit: this file is overwritten on the next generation.
import type { Account } from '../models/account.js';
/** Sent when an account is closed. */export type AccountClosedWebhookPayload = Account;
/** The request the API sends for the `accountClosed` webhook. */export type AccountClosedWebhookRequest = { /** Request body. */ body: AccountClosedWebhookPayload; /** Declared headers (names in lowercase). */ headers: { 'x-signature': string };};
/** Response body the API expects from the `accountClosed` webhook (`200`). */export type AccountClosedWebhookResponse = { received?: boolean;};
/** * Handles the `accountClosed` webhook. * * Implement it in your server and call it with the received request. Payloads are not validated; use the generated Zod schemas to validate untrusted input. */export type AccountClosedWebhookHandler = (request: AccountClosedWebhookRequest) => AccountClosedWebhookResponse | Promise<AccountClosedWebhookResponse>;