Frontmatter
Every template starts with a YAML mapping between two --- lines. It names the template and its family, and carries the envelope text: the subject, the inbox preview line and, for some families, the sender or the issue number.
---
template: payout.sent
family: seller
subject: "Payout sent: {{amount}}"
preheader: "{{order_count}} orders, {{period}}. In your bank within 48 hours."
footer:
note: Questions about this payout? Reply to this email.
---
Look up a key
| Key | Type | Required | Notes |
|---|---|---|---|
template | string | yes | Must equal the name passed to Compile. |
family | string | yes | A family name in the registry passed to Compile. |
subject | text | yes | Merge tags allowed. At render, a subject that is empty or contains CR, LF or NUL is an error. |
preheader | text | recommended | The inbox preview line. Leaving it out is the warning frontmatter: no preheader. A tag with no value renders as nothing here rather than failing. |
from | text | family-dependent | Required, and only allowed, when the family has FromFrontmatter. |
issue | text | family-dependent | Required, and only allowed, when the family has an H3 masthead. It fills {issue} in the masthead. |
legal | boolean | no | true switches to the family's Legal override: its stream, footer and copy. |
footer.note | Markdown | no | The F2 lead line. |
footer.about | text | no | The F2 {about} value in "This is a service email about …". "" drops that line. |
footer.reason | Markdown | no | The F1 "why you're getting this" line. |
footer.nav | boolean | no | Turns the F1 nav row on or off. |
footer.optout | Markdown | no | The F3 opt-out line. |
"Text" is a YAML string that may hold merge tags. "Markdown" is a string of exactly one paragraph of inline Markdown; more than that is frontmatter: "footer.note" must be one paragraph. footer itself must be a mapping.
A key of the wrong YAML type is an error at the key: frontmatter: "subject" must be a string, frontmatter: "legal" must be a boolean, frontmatter: "footer.nav" must be a boolean. Unknown keys, at the top level or under footer, are errors too: frontmatter: unknown key "colour", frontmatter: unknown key "footer.links".
1:1, because there is no key to point at: frontmatter: missing "subject". Every other frontmatter error points at the key's own line and column. A merge-tag error in a one-line subject, preheader, from or issue points at the tag itself; in a footer.* value or a multi-line YAML string it points at the key.Match the name
template exists so the file and the Go code can't drift apart. If you compile the file under another name, the compile fails:
payout.sent:2:1: frontmatter: template "payout" does not match name "payout.sent"
Use placeholders in footer overrides
The footer.* strings are shown in the family's footer, so they use the same {name} placeholders as the family's own footer copy — {site}, {brand}, {domain}, {region}, {address}, {about}, {prefs}, {unsubscribe} and {issue} — filled per message at render time. They can also hold merge tags.
footer:
note: "Payment is held until you confirm delivery. Questions? Visit the [Help Centre]({site}/help)."
An unknown placeholder is a compile error at the key: unknown placeholder "{sitee}". Placeholders work only in footer.* and in family copy. Anywhere else in a template, {site} is plain text. The values are listed in Families and streams.
Check what the family accepts
Four keys depend on the family, and lttr.Compile checks them against the registry you pass in (D29). The rules come from the family's fields, not its name:
| Family setting | Frontmatter rule | Error when broken |
|---|---|---|
FromFrontmatter: true | from is required. | frontmatter: family "b2b" requires "from" |
FromFrontmatter: false | from is not allowed. | frontmatter: "from" is only allowed for families that take it |
Header: H3 | issue is required. | frontmatter: family "newsletter" requires "issue" |
Header: H1 or H2 | issue is not allowed. | frontmatter: "issue" is only allowed for families with a masthead |
Legal: nil | legal: true is not allowed. | frontmatter: family "buyer" has no legal variant |
| footer variant | Only the footer.* keys that footer shows. | frontmatter: "footer.reason" is not used by the footer of family "buyer" |
family not in the registry | — | frontmatter: unknown family "nope" |
The footer variant decides which overrides apply. With legal: true, it is the variant of the family's Legal override:
| Footer | Accepted footer.* keys |
|---|---|
F1 (marketing) | reason, nav |
F2 (service) | note, about |
F3 (outreach) | optout |
F3Internal | none |
A literal from is also parsed as an address at compile time: frontmatter: "from" is not a valid address: …. A from with merge tags is parsed at render, and every from must be on the family's sender domain (SenderDomain, or Config.Domain), which is checked at render too.
See it in the Quiet example
The nine families of examples/quiet cover every rule:
| Family | Header | Footer | from | issue | legal | footer.* |
|---|---|---|---|---|---|---|
buyer, seller, driver, security | H1 | F2 | no | no | no | note, about |
announcement | H1 | F1 (F2 with legal: true) | no | no | yes | reason, nav (note, about with legal: true) |
newsletter | H3 | F1 | no | required | no | reason, nav |
promo | H1 | F1 | no | no | no | reason, nav |
b2b | H1 | F3 | required | no | no | optout |
internal | H2 | F3Internal | no | no | no | none |
Two of the examples use the family-dependent keys:
template: journal.digest
family: newsletter
issue: "{{issue}}"
template: outreach.partner
family: b2b
from: "Marisa Hodge <marisa@partners.caribhubs.com>"
Next steps
- Modules for the body of the template.
- Families and streams for defining the families these rules come from.