Browse the docs

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).

curl -X POST "https://api.workwae.com/v1/templates" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "name": "welcome",
  "description": "First mail after sign-up",
  "subject": "Welcome, {{params.firstName}}",
  "previewText": "Your {{params.plan}} plan is ready",
  "html": "<p>Hi {{params.firstName}},</p><p>Welcome aboard. {{account.name}}</p>",
  "text": "Hi {{params.firstName}},\n\nWelcome aboard. {{account.name}}"
}'

Variables and escaping

VariableValue
{{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).
  • html and previewText escape {{...}} values for HTML; {{{...}}} inserts a value raw. subject and text are not escaped.
  • previewText is 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_failed with the field in data.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.

SDK
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.*}}.

curl -X POST "https://api.workwae.com/v1/template-renders" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "templateId": "'"$TEMPLATE_ID"'",
  "accountId": "'"$ACCOUNT_ID"'",
  "params": { "firstName": "Ada", "plan": "Pro" }
}'

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.

SDK
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/templates lists templates newest first, without their content: usableBy keeps those an account may send with (the shared ones and its own), accountId those bound to an account, name an exact name and query a name substring.
  • GET /v1/templates/{templateId} reads one with its content.
  • PATCH /v1/templates/{templateId} changes the fields given; DELETE removes it. Queued sends keep what they were rendered with.
SDK
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}}" });