Email templates and sending
On this page
This topic has the exact rules for how an email's templates are checked and how the email is sent (Email has the overview).
Rendered when it is sent
The outbox keeps an email's fields as its payload: its type's contract JSON, within the outbox's 64 KB limit (Limits and runtime faults). Its delivery renders it as it is sent: its templates, its subject and its attachments. A rendered body never fills the outbox, and a retry renders the same text from the same fields.
A template is a callable
Each .html and .txt is read beside its type, as a prompt's text is
(Prompts as files). The body checker checks
it as a pure callable taking its email's fields, with to and locale
first, and lowers it to a typed body the executor runs, so its
expressions are Jadpo's own, typed once. The layout's callable takes
content, subject and locale.
At least one template
An email has an .html, a .txt or both. These are
email.template_missing: an email with neither, a template named after
no email, and a file in shared/emails/ other than the layout's two.
What a template may do
A template may use literals, its names and their fields, comparisons,
and, or, not, enum values, translations and text.format.
Arithmetic, any other call, building a value, and reading a setting or
a constant are email.template_not_allowed. Translations and
text.format take the email's locale when locale: is left out.
{{ locale.direction }} is Jadpo's wording of TextDirection
(jadpo.text_direction): ltr or rtl, unless the root translations
file rewords it. {{ app.display_name }} is
application { display_name: }, plain text on one line.
What is written out
- A value that may be
noneis refused. - A number is written by
text.formatin the email's locale. - An enum is written by its wording.
Htmlis written as it is in an.htmltemplate, and refused in a.txtone.- Everything else is escaped in HTML:
&,<,>,"and'become character references.
A subject in every locale
An email's entry in its domain's language files is required in every
supported locale that isn't in progress, as a message's wording is
(translation.wording_missing).
A field nothing reads
A field nothing reads is email.field_unused, which you can decline.
to, locale and File fields, which are attachments, are never
reported, and a field the subject's wording writes in any locale counts
as read.
The layout
When the layout exists, it wraps each body: the .html in
layout.html and the .txt in layout.txt. You can keep either file
without the other. Without a layout, "Jadpo's plain layout" is the
template's own text, sent as it is.
email:
email: names a connection with driver: Driver.smtp, or a mail
package's connection that fills email. Its from: is an Email
setting or an address written out. Emitting an email with no email: is
email.connection_required.
Its delivery
Each email type has a subscriber of its own, email <domain>.<Type>,
tried as a subscriber is by default: 10 attempts within 24 hours. Its
record alone says it went.
The delivery renders the email first, so an email that can't be
rendered is settled as a subscriber's fault is, and nothing is sent.
Then it is marked sending and sent with no key. The driver is given
from, to, subject, html, text and attachments: each
attachment's name, media type and bytes, read whole from its store, at
most 10 MB, as store.read reads.
Development and tests
jadpo test gives the email connection a driver that catches each
message and answers that it went, so every email is rendered and nothing
leaves your machine. jadpo dev catches mail the same way, in the dev
inbox.
Where a partial is declared
A partial such as
type Button = EmailPartial { label: PresentationText url: Url } is
declared in shared/emails/ or a domain's emails/, with its .html
and .txt beside it as an email's are, and called with its fields by
name.
Markdown for agents: /docs/email-templates-and-sending.md.Built from Jadpo main at a707efb.