← All @molecule/* packages · App templates
@molecule/api-email-templatesCore interface · email-templates · API (Node) · v1.0.1 · Apache-2.0
Transactional email templates: i18n-driven subject/text/html templates with variable interpolation, sent via the bonded email transport
npm install @molecule/api-email-templates@molecule/api-email-templates is the email-templates core interface on the API (Node) side: the API your app calls, with no vendor inside.
Bond a provider to choose the implementation.
import { sendTemplate, TEMPLATE_KEYS } from '@molecule/api-email-templates'
await sendTemplate(TEMPLATE_KEYS.subscriptionStarted, {
from: 'support@example.com',
to: 'user@example.com',
locale: 'en',
variables: {
appName: 'Personal Finance',
userName: 'Lou',
planName: 'Pro',
amount: '$19.00',
period: 'month',
manageUrl: 'https://app.example.com/billing',
},
})Works with: @molecule/api-emails, @molecule/api-i18n
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Transactional email templates for molecule.dev.
Provides a small registry of i18n-driven templates (subscription started,
renewed, canceled, payment failed, usage-limit warning, trial ending) plus
a sendTemplate(...) convenience wrapper around the bonded
@molecule/api-emails transport.
Apps can override individual templates by calling registerTemplate(...)
with the same key, register entirely new templates, or render templates
directly via renderTemplate(...) for delivery channels other than email.
import { sendTemplate, TEMPLATE_KEYS } from '@molecule/api-email-templates'
await sendTemplate(TEMPLATE_KEYS.subscriptionStarted, {
from: 'support@example.com',
to: 'user@example.com',
locale: 'en',
variables: {
appName: 'Personal Finance',
userName: 'Lou',
planName: 'Pro',
amount: '$19.00',
period: 'month',
manageUrl: 'https://app.example.com/billing',
},
})
core
npm install @molecule/api-email-templates @molecule/api-emails @molecule/api-i18n
EmailTemplateDefinition of a transactional email template.
Each template defines i18n keys for the subject and body fields plus
English defaults that are used when no translation is registered for the
caller's locale. Variable interpolation is performed by the i18n library
(e.g. Welcome, {{userName}}! substitutes vars.userName).
interface EmailTemplate {
/** Stable identifier for the template (e.g. `'subscription.started'`). */
key: string
/** i18n key resolved by `t()` for the email subject line. */
subjectKey: string
/** English fallback used when no translation exists for `subjectKey`. */
defaultSubject: string
/** i18n key resolved by `t()` for the plain-text body. */
textKey: string
/** English fallback used when no translation exists for `textKey`. */
defaultText: string
/** Optional i18n key resolved by `t()` for the HTML body. */
htmlKey?: string
/** Optional English fallback used when no translation exists for `htmlKey`. */
defaultHtml?: string
/**
* Optional declaration of the variables this template expects. Used purely
* for type inference at registration sites — runtime rendering is permissive.
*/
variables?: readonly string[]
}
RenderedEmailResult of rendering a template — ready to feed into EmailTransport.sendMail.
interface RenderedEmail {
/** Rendered subject. */
subject: string
/** Rendered plain-text body. */
text: string
/** Rendered HTML body, when the template defined one. */
html?: string
}
SendTemplateOptionsOptions passed to sendTemplate.
interface SendTemplateOptions {
/** Recipient address (string or `EmailAddress`). */
to: string | EmailAddress | (string | EmailAddress)[]
/** Sender address. */
from: string | EmailAddress
/** Optional CC recipients. */
cc?: string | EmailAddress | (string | EmailAddress)[]
/** Optional BCC recipients. */
bcc?: string | EmailAddress | (string | EmailAddress)[]
/** Optional reply-to address. */
replyTo?: string | EmailAddress
/** Variables interpolated into the rendered template. */
variables?: EmailTemplateVariables
/**
* Optional locale override used when looking up translations. Most apps
* resolve locale from the request and pass it explicitly.
*/
locale?: string
}
EmailTemplateVariablesVariables passed at render time. Values are flattened to strings for substitution; objects/arrays are JSON-stringified.
type EmailTemplateVariables = Record<string, unknown>
TransactionalTemplateKeyType of every key declared in TEMPLATE_KEYS. Use this for type-safe
sendTemplate(...) calls.
type TransactionalTemplateKey = (typeof TEMPLATE_KEYS)[keyof typeof TEMPLATE_KEYS]
clearRegistry()Drop every override. The built-in defaults remain available via getTemplate.
Intended for tests; production code should not need to call this.
function clearRegistry(): void
getTemplate(key)Look up a template by key. Returns the registered override if any, then
falls back to the built-in defaults, and finally returns undefined when
no template is known.
function getTemplate(key: string): EmailTemplate | undefined
key — The template key.Returns: The matching template, or undefined if unregistered.
listTemplates()Returns every template currently visible — overrides, then any built-in defaults whose key wasn't overridden.
function listTemplates(): EmailTemplate[]
Returns: A snapshot of all available templates.
registerTemplate(template)Register or override a single template. Subsequent calls with the same
key replace the previous registration.
function registerTemplate(template: EmailTemplate): void
template — The template to register.registerTemplates(templates)Register or override several templates in one call.
function registerTemplates(templates: EmailTemplate[]): void
templates — The templates to register.renderTemplate(template, variables, locale)Render a template into a RenderedEmail ready to send.
function renderTemplate(
template: EmailTemplate,
variables?: EmailTemplateVariables,
locale?: string,
): RenderedEmail
template — The template to render.variables — Optional values used for {{variable}} interpolation.locale — Optional locale override (e.g. 'fr'); defaults to the bonded i18n provider's current locale.Returns: The rendered subject, text, and (optional) html.
sendTemplate(key, options)Render a template and send it via the bonded email transport.
Looks up the template via getTemplate(key) (registered overrides first,
then built-in defaults), renders it for the requested locale + variables,
and dispatches the rendered message via the bonded transport.
function sendTemplate(key: string, options: SendTemplateOptions): Promise<EmailSendResult>
key — The template key (e.g. 'subscription.started').options — Recipient, sender, optional cc/bcc/replyTo, variables, locale.Returns: The transport's EmailSendResult.
defaultTemplatesBuilt-in templates indexed by key. Read by getTemplate(key) whenever
the runtime registry has no override registered.
const defaultTemplates: Record<string, EmailTemplate>
TEMPLATE_KEYSIdentifier of the built-in subscription lifecycle templates.
const TEMPLATE_KEYS: {
readonly subscriptionStarted: 'subscription.started'
readonly subscriptionRenewed: 'subscription.renewed'
readonly subscriptionCanceled: 'subscription.canceled'
readonly paymentFailed: 'subscription.paymentFailed'
readonly usageLimitWarning: 'subscription.usageLimitWarning'
readonly trialEnding: 'subscription.trialEnding'
}
Peer dependencies:
@molecule/api-emails ^1.0.1@molecule/api-i18n ^1.0.1@molecule/api-emails@molecule/api-i18nThis is a library on top of two bonds — BOTH must be wired before
sendTemplate() works:
@molecule/api-emails transport bonded (sendTemplate dispatches via
the bonded transport and throws when none is wired) and
@molecule/api-i18n bonded (subject/text/html resolve through t()).sendTemplate throws on an unregistered key. Built-ins cover only the
subscription lifecycle (TEMPLATE_KEYS.*). Any other template must be
registered at startup first:
registerTemplate({ key, subjectKey, defaultSubject, textKey, defaultText, ... }).
Overriding a built-in = registering with the SAME key (overrides win).{{variable}} via i18n. Pass every variable the
template references in variables; strings/numbers/booleans/Dates pass
through, anything else is JSON-stringified.renderTemplate(...) and
dispatch the RenderedEmail yourself.Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual emails/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
{{placeholder}},
undefined, or [object Object] survives in the subject, text, or html —
and the html and text parts carry the same copy/links (consistent).sendTemplate with that record's real data — never a mock. The sandbox
CAPTURES outbound email instead of delivering it — read it with the
read_activity tool (filter type 'email') and confirm the captured
subject + body match the template rendered with that record's data. Don't
modify production code to expose the send.{{placeholder}},
undefined, or [object Object] is NEVER shipped in the delivered email.<script> / <img onerror=...> / {{amount}} as a name renders as inert
text, never live markup or a second interpolation (no HTML/template
injection). No secret (API key, transport credential, token) ever appears
in a rendered subject or body.