Templates

Reusable email templates with Handlebars, immutable versions, shared partials and an optional AI assist that drafts a template from a plain-English prompt.

How templates work

A template is a saved (slug, subject, body) triple, scoped to your product. Every save creates a new immutable version — older versions stay queryable so in-flight sends are deterministic even mid-edit.

You send by slug; the platform resolves the currently active version at send time and pins it into the queued message. Newsletters pin the version at fire time so a paused-and-resumed broadcast uses one consistent version end-to-end.

Create a template

POST /api/v1/templates
{
  "slug":    "welcome",
  "subject": "Welcome to {{product_name}}, {{name}}!",
  "body":    "<!DOCTYPE html>\n<html>..."
}

Slug rules: lowercase letters, digits, hyphens. Must be unique within the product. Once created, the slug is immutable — to "rename," create a new template and migrate references.

Handlebars syntax

Both subject and body are rendered through Handlebars at send time. You get:

  • {{variable}} placeholders
  • Conditionals: {{#if foo}}...{{/if}}
  • Loops: {{#each items}}...{{/each}}
  • Partials: {{> my-partial}}

Always-available variables

VariableMeaning
{{name}}Contact's display name (or the synthetic preview name during preview).
{{email}}Contact's email address.
{{unsubscribeUrl}}Per-list one-click unsubscribe URL. Used inside the partial, not directly.

Anything in the API call's variables object is also available. Unknown placeholders render as empty strings — they don't crash the send.

Partials

Reusable HTML blocks that live at the product level and can be included in any template:

{{> header}}
{{> product-card}}
{{> unsubscribe-footer}}
The unsubscribe-footer partial is mandatory on every marketing email. It renders the compliant unsubscribe block (CAN-SPAM, RFC 8058 one-click) and lets us track per-list opt-outs. The AI generator refuses to produce a body without it.

HTML rules for inbox-safe email

  • Use a full HTML document starting with <!DOCTYPE html>.
  • Inline all styles — most email clients (Gmail web, Outlook desktop) strip <style> blocks.
  • Table-based layout for max compatibility. CSS grid and flexbox are unreliable in Outlook.
  • Keep visible content under 600px wide.
  • System fonts only unless you accept a fallback. Embedded fonts don't render in Outlook.

AI template generator

Skip the boilerplate: describe what you want in plain English and Mailazy drafts a working template (subject + slug + body) — inline-styled, table-laid, with the unsubscribe partial wired in.

Configure once

Bring your own OpenRouter API key (one-time, per product) under Settings → AI. Choose a model (any OpenRouter-compatible model; Claude Sonnet and Haiku work especially well for HTML email). Mailazy never proxies through our LLM provider — your bill is between you and OpenRouter.

Generate from a prompt

From the Templates page, click + Generate with AI. The modal asks for:

  • Prompt — e.g. "Welcome email for SaaS signup, friendly but professional, ~120 words, CTA to dashboard"
  • Additional variables — any Handlebars vars the AI should use beyond the always-on set, e.g. product_name, dashboard_url, plan
  • Tone hint (optional) — "casual," "formal," "no emoji"

Hit Generate. The right pane fills with a live iframe preview. Click Regenerate to retry, Edit body in raw editor to tweak, or Save as template to commit. Nothing is saved automatically — every generation is a draft until you press Save.

Per-tenant brand voice

Set a defaultSystemPrompt on your AI config to inject brand-voice rules into every generation (e.g. "always end with the line 'Built in Bengaluru.'"). It's appended to the built-in rules, never replacing them.

Rate limits

20 generations per minute per product. Bumps available on Scale tier.

Preview and test-send

Every saved template has a View action that opens a live preview using a synthetic contact (or any real contact id you paste). The preview renders through the exact same Handlebars + partial pipeline as a production send — what you see is what gets delivered.

Send a test email

Inside the preview modal, drop an address in Test send to and click Send. The email routes through your tenant SMTP exactly like a real send, but does not write a send_logs row, does not increment newsletter counts, and (intentionally) does not check suppressions — test sends are operator-initiated and meant to verify rendering.

Programmatic equivalent:

POST /api/v1/templates/{id}/preview      → { subject, html, fromAddress }
POST /api/v1/templates/{id}/test-send    → { to, sentAt }

Rate limit on test-send: 10 / minute / template, returning 429 with Retry-After.

Versioning

Every POST /api/v1/templates with an existing slug creates a new active version; the prior version is retained for audit and for in-flight messages. Query by version with GET /api/v1/templates/{slug}?version=4.

To roll back, send the previous body as a fresh save — there's no destructive delete on versions. Templates marked inactive disappear from dropdowns but remain queryable.

Related