Docs/Sending

Define events

Declare every event once, as a schema. The event name types the data you send and the payload your consumers receive.

Events are a plain object. Each key is an event name, each value is the schema of that event's data.

src/events.ts
import { z } from "zod";

export const events = {
  "invoice.paid": z.object({ invoiceId: z.string(), amountCents: z.number() }),
  "member.invited": z.object({
    email: z.string().email(),
    role: z.enum(["admin", "viewer"]),
  }),
};

Pass it to webhooks({ events }). From then on, send() only accepts known event names, and the name narrows data:

await wh.send({
  event: "member.invited",
  subject: "acct_42",
  data: { email: "ana@northwind.dev", role: "admin" },
});

Any Standard Schema

The SDK only talks to the Standard Schema interface, so zod (3.24 or later), valibot and arktype all work, and you can mix them in one map.

import * as v from "valibot";
import { type } from "arktype";

export const events = {
  "invoice.paid": v.object({ invoiceId: v.string(), amountCents: v.number() }),
  "invoice.voided": type({ invoiceId: "string" }),
};

What gets validated

send() validates data against the event's schema before anything else happens. Nothing is sent, and nothing is written to the outbox, when it fails:

Problemresult.error
An event name not in the mapunknown event type "…"
Data that fails the schemainvalid payload for "…": and the issue messages
The validator itself throwsschema validation threw for "…": …
Output that can't be serializedpayload for "…" is not JSON-serializable: …

The validated output is what gets sent. Defaults and transforms in your schema run before the payload is signed.

Types on the wire

The body is JSON, so some types change on the way. A z.date() field is sent as an ISO string, and a bigint can't be serialized at all (send() returns an error). EventUnion describes the payload as consumers receive it, after that round trip:

import type { EventUnion } from "@typedhook/sdk";

export type Events = EventUnion<typeof events>;
// { event: "invoice.paid"; subject: string | null; data: { invoiceId: string; amountCents: number } } | …

See Typed payloads for how consumers use it.

Edit on GitHubLast updated on October 14, 2026

On this page

For coding agents

Every page is available as Markdown. Point your agent at the full docs in one file.