Templates
Handlebars and Markdown templates: variables, escaping, previews and sending with them.
Create a template
A template is a named, stored subject, html, text and previewText, each written in Handlebars. It belongs to the tenant and is usable by every account, or, with accountId, bound to one account. Names (up to 255 characters) are unique per scope (template_name_taken). Every field must compile when saved, and partials and decorators are refused (invalid_template, whose data.field names the field).
Variables and escaping
| Variable | Value |
|---|---|
{{params.*}} | The params of the send, merge entry or render, up to 256 KB as JSON. |
{{account.email}}, {{account.name}} | The sending account's address and name. |
{{rcpt.unsubscribeUrl}}, {{rcpt.address}}, {{rcpt.name}} | The recipient's one-click unsubscribe link, address and name, on sends naming a listId (empty otherwise). |
htmlandpreviewTextescape{{...}}values for HTML;{{{...}}}inserts a value raw.subjectandtextare not escaped.previewTextis injected at the top of the HTML body as a hidden block: the line inboxes show next to the subject.- Rendering is bounded: at most 10,000 block iterations per render, and each field has a size limit. A render that breaks one answers
template_render_failedwith the field indata.field.
Markdown templates
With format: "markdown", html holds Markdown: Handlebars renders it first (escaping values for HTML and Markdown, so params add text, never markup), then up to 100,000 characters are converted to HTML. Without a text, the plain-text part is the rendered Markdown.
await workwae.templates.create({
name: "weekly-digest",
format: "markdown",
subject: "Your week, {{params.firstName}}",
html: `# Hi {{params.firstName}}
{{#each params.items}}
- **{{this.title}}**: {{this.summary}}
{{/each}}
[Unsubscribe]({{rcpt.unsubscribeUrl}})`,
});Preview without sending
POST /v1/template-renders renders a stored template (templateId) or inline fields with params, exactly as a send would, and keeps nothing. Pass accountId to fill in {{account.*}}.
Send with a template
Sends, drafts and mail merges name a template with template (its id), and sends and drafts pass values with render.params; a merge passes them per entry. A template's fields replace the inline ones, which cannot be given with it. The content is rendered when the request is taken: editing or deleting the template later never changes queued mail.
await workwae.outbox.create({
accountId,
to: [{ name: "Ada", address: "[email protected]" }],
template: template.id,
render: { params: { firstName: "Ada", plan: "Pro" } },
});See Sending mail for mail merges.
List, update and delete
GET /v1/templateslists templates newest first, without their content:usableBykeeps those an account may send with (the shared ones and its own),accountIdthose bound to an account,namean exact name andquerya name substring.GET /v1/templates/{templateId}reads one with its content.PATCH /v1/templates/{templateId}changes the fields given;DELETEremoves it. Queued sends keep what they were rendered with.
import { paginate } from "@workwae/sdk";
for await (const summary of paginate(workwae.templates.list, { usableBy: accountId })) {
console.log(summary.name, summary.source, summary.updatedAt);
}
await workwae.templates.update({ templateId, subject: "Welcome to the team, {{params.firstName}}" });