The deliverable is the input contract
Each template declares a Zod payload schema — its documented, validated variable set, validated when a job is enqueued — plus a category and a kind. Bodies are Handlebars, and they include shared head and foot partials so every message inherits the theme and the Gmail-safe shell.
Visual polish is explicitly secondary. The thing you build against is the schema.
The twelve
All twelve are named in the reference, and every one of them reaches a recipient the same way — as a send started by a Workflow rather than by a request.
| Template | Category | Kind |
|---|---|---|
magicLink | transactional | transactional |
otp | transactional | transactional |
welcome | transactional | transactional |
securityAlert | transactional | transactional |
invite | transactional | transactional |
passwordChanged | transactional | transactional |
operationalNotice | transactional | transactional |
supportReply | transactional | transactional |
testerNudge | transactional | elective |
leadCapture | transactional | elective |
newsletter | marketing | elective |
marketingCampaign | marketing | elective |
The three rows in bold are why the two columns exist. A tester nudge and a lead-capture follow-up are transactional in style — plain, prompt, tied to something the person did — and elective in consent. One field could not have said both.
The kind is declared, never passed
There is no argument for it, and no default.
If is this transactional were something a caller supplied, a caller could get it wrong — and the failure mode is an account nobody can reach: a magic link sent as elective is a magic link an unrelated unsubscribe silently swallows.
Templates are the capability’s own, so declaring the kind on the template makes the wrong thing impossible to write rather than merely discouraged. And a forgotten field is a type error rather than a silent lockout.
The registry is closed to adopters
There is no registerTemplate. Three reasons, and the first is not a matter of taste:
The Workers runtime forbids code generation, so a template cannot be compiled where it runs. Every body is turned into a precompiled spec at build time. Accepting an adopter’s template means accepting a spec built by their Handlebars — and Handlebars refuses a spec whose compiler revision differs from the runtime’s. That is a version skew nobody would notice until every email failed to render at once.
The kind would go back to being a claim. The whole fix above rests on a call site being unable to assert that a message is transactional. A registerable template makes that assertion writable again, and the mail it produces ignores an unsubscribe — under your own sending domain.
Escaping is structural here, not conventional. testerNudge and supportReply are safe because their bodies are fixed and the words arrive as escaped values. A supplied body with one triple-brace is a phishing page sent over your DKIM signature.
Tracked links are declared, not detected
A template names the payload locations that hold URLs, each with a label. The engine rewrites those to the tracking callback, and the label is what lands on the click event for attribution.
A location is a top-level key — "url" — or a field on each element of an array — "articles[].link".
Nothing is rewritten by scanning the rendered HTML for anchors. A declared location is a decision somebody made; a scanned one is a guess that eventually rewrites a link in a signature, a footer, or somebody’s quoted reply.
Width is a property of the message, not the brand
Each template declares the body width it renders at. A one-line security alert and a newsletter want different measures, and that difference belongs to the email type rather than to the theme — so switching themes never re-flows a template into a shape it was not written for.
Where the words come from
Copy is translated through the kit’s own message layers, so a template renders in the recipient’s language when one is known. The email is written in the language the job recorded, not in the language of whoever happened to trigger it — a receipt for a Spanish-speaking customer generated by an English-speaking admin’s action is still in Spanish.