Skip to main content
Every payload you send to Brew has a contract. A custom trigger declares one up front (its payloadSchema); a transactional email derives one from the pinned template (every trigger.* path the design reads). This guide turns those contracts into TypeScript types in your codebase, keeps them from drifting, and shows where Brew checks real fires against them.
Nested payload values (objects and arrays) require a Liquid-enabled workspace. The same nested shape sent to a legacy workspace is rejected with 400. See Merge tags and variables.

Generate types with brew-cli

brew-cli types writes one file with a type per contract (the command needs a key with the automations scope; --transaction also needs sends):
Triggers are included automatically (every page of the workspace list — nothing is truncated). Transactional objects are opt-in by id because their contracts derive from each pinned template. The output is deterministic, with a content hash in the header:
Type names follow one rule everywhere (the in-app Copy as TypeScript, the SKILL.md brief, and this file): PascalCase of the trigger title or the transactional subject, plus Payload. Renaming a subject renames the type, which is exactly the drift --check exists to catch; colliding names get the object id appended so the file always compiles. A template reference with no type evidence (no fallback, no numeric or boolean usage) is emitted as unknown for you to refine, never silently asserted as string. Field optionality follows each plane’s rules. Trigger fields are optional when required: false. Transactional fields are optional when the template gives them a | default: fallback; a path with no fallback fails strict fires when the caller omits it, so it is emitted as required. In CI, gate drift with:
The command exits 1 when the workspace’s contracts no longer match the committed file, which is the signal to regenerate and review the diff. A trigger schema edit or a transactional design change shows up as a type change in code review instead of a runtime surprise.

Pin the types in SDK calls

fire and send accept a payload type parameter, so call sites compile against the contract:
A missing required field or a wrong scalar type is a compile error. The wire request is unchanged; the generics are type-level only.

Fetch the wiring brief for an agent

Both reads accept ?include=skill, which adds a skill field: a complete SKILL.md-shaped brief (endpoint, auth, the typed contract inline, copy-paste snippets, and a test loop) that a coding agent can follow to wire your service.
Save the field as SKILL.md in your repo, or hand the URL to an agent. The in-app contract panel offers the same file under Copy as → Download SKILL.md, and it is generated from the same source as the API response.

Preflight before the first fire

GET on the fire endpoint verifies the exact credential you will use, without firing:
A 200 with status: "ready" means the key, brand scope, and permissions all pass, and details carries the payload contract plus what a fire would start. counts.automations: 0 means fires are accepted and logged but start no runs until an automation wired to the trigger is published.

Verify live fires in the app

Brew checks what your service actually sent against the contract:
  • The transactional object page shows Recent fires vs contract: each retained fire’s raw payload, with chips for required fields the caller omitted, keys the template never reads, and strict-render failures with the recorded error.
  • A trigger fire’s detail sheet (Events page) shows a Contract check: the raw body replayed through the same validator the live fire used, so a key the fire dropped is labeled exactly as dropped.
  • Both object pages walk you through wiring with a step list whose completion is derived from real data: a key exists, your test fire arrived, an automation is live.
Together the loop is: generate types, pin them in calls, gate drift in CI, and read the live ledger when something looks off.