Transactional Email Templates
Managing React Email transactional templates from the content dashboard.
Yayaw manages transactional email templates from
/dashboard/content/email-templates. That route is a screen: a Yayaw Table
full-page table for searching, filtering, creating, activating, deactivating,
deleting, and opening templates (see The email templates list).
Each row opens the dedicated React Email editor at
/dashboard/content/email-templates/[slug].
Related docs:
CMS Overview for how templates share variables with the wider content system
CMS Data Models for global CMS variables
Billing Products for billing purchase and subscription events
The email templates list
/dashboard/content/email-templates (route permission email-template:list)
renders the screen dashboard.content.email-templates. The templates are the
platform's, so it is a global screen: one copy for the whole platform, which
only Super Admins customize (see Dashboard). Its
default is the full-page table on the system:email-templates source, the
templates edited last first, 20 a page.
The table lists every template to whoever holds email-template:list
globally, whatever organization is active, and an organization's grant
never opens it. Whoever also holds
email-template:manage globally (Super Admins) gets:
New template: the create form (name, slug made from the name when left empty, category), then the new template's editor;
a row's deletion, and for a selection Delete, Activate and Deactivate, each confirmed; activating or deactivating changes only the templates not already so.
The server checks every write again (email-template:create, delete or
update, globally).
The table keeps the list's columns (name, slug, category, status, variables,
updated), its saved views and favorites (email-templates-catalog-v1), and
its work queues after the screen's own view: Active templates, Inactive
templates and Transactional email. It offers Table, List and a Kanban by
category, whose cards cannot be dragged: a template's category is changed in
its editor.
Search reads the name, slug and category, and every language's subject and description. It never reads a template's HTML, plain text or editor document.
Paging: at most 100 templates a page (10, 20 or 50 offered).
Order: names sort as people read them ("Welcome 2" before "Welcome 10"), in the database; where two names differ only by case or accents, the lowercase, unaccented one comes first.
URL parameters: the table's parameters are keyed by its source (
system:email-templates-…), andview=<id>opens a saved view or a work queue.
An assistant preparing a draft of this screen reads the source's columns only, never a row's id or whether the viewer may change it; assistants list and edit templates with the tools below.
CMS Role
Transactional email templates are content-managed communication surfaces. They share authoring, localization, variables, and control-plane patterns with the CMS, but they do not render through the public page runtime. Runtime senders load active templates, apply transactional variables, resolve CMS variables, and fall back to React Email component defaults when no database template exists.
Use email templates for purchase confirmations, subscription updates, invitations, and other event-driven messages. Use pages and sections for public or organization-member web content.
Data Model
Each template stores:
slug,name,subject,preview_text,description,categoryrendered
htmland plaintextexports used by runtime sendingeditor_document, the React Email Editor TipTap JSON documentdefault_locale,locales, andlocalized_contentfor inline translated variantsvariables, the template-specific{{variable}}contractsample_variables, used by the external preview and test sendslegacy
design, kept only so older Unlayer rows can migrate safely
Templates are platform-wide in v1. Organization-specific overrides should be added later as an explicit lookup layer rather than overloading the current global table.
The production control plane exposes template operations for trusted MCP clients:
yayaw_email_templates_listyayaw_email_template_getyayaw_email_template_saveyayaw_email_templates_sync_system
Writes require control-plane:write or control-plane:admin, Yayaw
email-template:update/manage authorization, and a reason.
Localization
Email templates follow the same inline localization model as CMS pages. The
editor toolbar lets authors switch between enabled locales, and each locale
stores its own subject, preview text, description, HTML, text export, and editor
document inside localized_content.
Saving a locale updates that locale only. The default locale also mirrors the legacy top-level columns so existing runtime lookups and older rows continue to work during migration.
The AI panel includes an enabled-by-default locale sync switch. When a template
is saved, the currently open locale is used as the source and the same copy,
structure, styling, and block-order changes are applied to the other enabled
locales. AI-generated sync updates write localized subject, previewText,
description, html, and text values; target-locale editorDocument values
are cleared so the editor can rebuild from the synchronized HTML when that
locale is opened.
The editor canvas focuses on design only. The toolbar opens the rendered email preview in a separate browser tab after exporting the latest editor state and applying sample transactional variables plus global CMS variables.
Variables
Email templates support two variable families:
Template variables use
{{name}}or nested paths such as{{invitation.inviteLink}}.CMS variables use the global CMS token format, for example
{site.name}or{data.global.header-menu.main.brand_label}.
The email editor exposes both families. CMS variables are resolved through the same published global-data and site-variable catalog used by pages and sections, so content authors can reuse the same global values across the CMS.
Runtime Behavior
When sending an email, Yayaw first tries the active database template for the configured slug and requested locale. If that locale is missing, rendering falls back to the template default locale and then to the legacy top-level columns. If no active template exists, auth emails keep using their React Email component fallback.
Billing emails are also system templates. They are sent from Stripe webhook handlers and are intentionally non-blocking: if Resend is not configured or the template is missing, the webhook logs the skip and continues processing the purchase or subscription update.
Built-in billing template slugs:
billing-purchase-confirmationbilling-subscription-updatedbilling-invoice-available
Billing templates can use nested variables such as:
{{billing.productName}}{{billing.amountFormatted}}{{billing.billingInterval}}{{billing.billingReason}}{{billing.optionsSummary}}{{billing.lineItemsSummary}}{{billing.discountSummary}}{{billing.periodStart}}and{{billing.periodEnd}}{{billing.invoiceId}},{{billing.invoiceNumber}},{{billing.invoiceUrl}}, and{{billing.invoicePdfUrl}}{{billing.manageUrl}}{{billing.accessUrl}}{{billing.deliverablesSummary}}{{organization.name}}{{user.email}}
billing-invoice-available is sent from Stripe invoice.paid events. The
handler is idempotent through billing_webhook_events and resolves the
organization from invoice metadata, subscription metadata, then local
subscription/customer rows. One-time Checkout enables Stripe invoice creation so
paid lifetime purchases can also generate this template.
Use bun run docs:llm:generate and the yayaw_email_templates_sync_system
control-plane tool after adding billing template metadata so the editor
variables and MCP control-plane sync stay aligned.
Stored HTML is treated as the fast runtime representation. Save the template
after editing so html, text, and editor_document stay synchronized.
Local Workflow
After changing template contracts, system template metadata, schema, or generated assistant docs, run:
bun run generate:actions
bun run docs:generate
bun run docs:check-translations
bun run docs:llm:generate
bun run docs:llm:check
bunx tsc --noEmit
bun run checkFor billing email variables, also run:
bun test src/lib/server/services/billing/billing-email-notifications.test.ts