Skip to main content

Templates

Email templates let you send flexible, custom emails to many different recipients in a single request. MailChannels currently supports Mustache templates.

Enabling templates

Two changes turn a regular send into a template send. 1. Mark the content part as a template.
"content": [
  {
    "type": "text/plain",
    "value": "Hello {{name}}",
    "template_type": "mustache"
  }
]
The template type field must be set in each content part that contains a template. Otherwise, that content part will not be modified, and any templating within will be sent as normal text. The JavaScript SDK avoids this by applying template.type to all content parts.
2. Supply per-recipient data. Add per-recipient template data to each personalization. The keys must match the placeholders in your template:
"personalizations": [
  { "to": [{ "email": "alice@example.com" }], "dynamic_template_data": { "name": "Alice" } },
  { "to": [{ "email": "bob@example.com" }],   "dynamic_template_data": { "name": "Bob"   } }
]
Placeholders without a matching key in dynamic_template_data render as empty strings.

Full example

#!/usr/bin/env bash
set -u
: "${MAILCHANNELS_API_KEY:?Set MAILCHANNELS_API_KEY before running}"
: "${FROM_EMAIL:?Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)}"
: "${TO_EMAIL_1:?Set TO_EMAIL_1}"
: "${TO_EMAIL_2:?Set TO_EMAIL_2}"

curl -X POST https://api.mailchannels.net/tx/v1/send \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $MAILCHANNELS_API_KEY" \
  -d @- <<JSON
{
  "personalizations": [
    { "to": [{ "email": "$TO_EMAIL_1" }], "dynamic_template_data": { "name": "Alice" } },
    { "to": [{ "email": "$TO_EMAIL_2" }], "dynamic_template_data": { "name": "Bob"   } }
  ],
  "from":    { "email": "$FROM_EMAIL" },
  "subject": "Hello from MailChannels",
  "content": [
    {
      "type": "text/plain",
      "value": "Hello {{name}}",
      "template_type": "mustache"
    }
  ]
}
JSON
Alice receives Hello Alice and Bob receives Hello Bob.

Testing checklist

  • Render the template with typical, empty, and unusually long values.
  • Verify links and unsubscribe behavior.
  • Preview in common email clients.
  • Check message size before adding attachments.
  • Confirm Gmail does not clip long messages.
  • Send a test through a real domain with SPF, DKIM, and DMARC configured.

Mustache features

Mustache is a simple and flexible templating language that allows you to create custom messages for all your recipients. See the Mustache spec for the complete reference; features not in the supported features list at the bottom of this page are not available.

Nested fields

Use . to access nested fields. Data
"dynamic_template_data": {
  "name": { "title": "Ms.", "first": "Jane", "last": "Doe" }
}
Template
Dear {{name.title}} {{name.last}}, your account name is {{name.first}} {{name.last}}.
Result
Dear Ms. Doe, your account name is Jane Doe.

Lists

Wrap a section in {{#list}}…{{/list}} to repeat it for each item. Inside the section, unqualified keys refer to fields of the current item. Data
"dynamic_template_data": {
  "items": [
    { "name": "Standard plan", "price": "9.99" },
    { "name": "Add-on seat",   "price": "4.00" }
  ]
}
Template
<ul>
{{#items}}<li>{{name}}: ${{price}}</li>
{{/items}}</ul>
Result
<ul>
<li>Standard plan: $9.99</li>
<li>Add-on seat: $4.00</li>
</ul>

Conditionals

The same {{#key}}…{{/key}} syntax renders the block only when key is truthy: boolean true, a non-empty list, or any non-false value. Other fields in the data can be referenced inside the block. Template
Thanks for being a customer!{{#anniversary}} You've been with us for {{years}} year(s) — here's a code: 50OFF{{/anniversary}}
With "anniversary": true, "years": 3:
Thanks for being a customer! You've been with us for 3 year(s) — here's a code: 50OFF
With "anniversary": false:
Thanks for being a customer!

Negated conditionals

The inverse — {{^key}}…{{/key}} — renders only when key is falsy or missing. Template
{{#verified}}Your account is verified.{{/verified}}{{^verified}}Verification failed.{{/verified}}
With "verified": true:
Your account is verified.
With "verified": false:
Verification failed.

HTML escaping

{{value}} HTML-escapes its output so injected content can’t break the layout. To insert content as raw HTML, use either triple braces or {{&value}}. Data
"dynamic_template_data": { "msg": "<b>Bold</b>" }
Template
escaped: {{msg}}
raw (triple):  {{{msg}}}
raw (ampersand): {{&msg}}
Result
escaped: &lt;b&gt;Bold&lt;/b&gt;
raw (triple):  <b>Bold</b>
raw (ampersand): <b>Bold</b>
Only emit raw HTML for content you control. Substituting recipient-supplied values without escaping is a common HTML injection vector.

Comments

{{! ... }} is stripped from the rendered output. Template
Welcome.{{! TODO: add personalization here }} Glad to have you.
Result
Welcome. Glad to have you.

Supported features

The Mustache features MailChannels supports, using the names from the Mustache spec:
  • Variables
  • Dotted names
  • Implicit iterator
  • Non-empty lists
  • Non-false values
  • Inverted sections
  • Comments
  • Set delimiter