> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mailchannels.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Templates

> Send personalized emails to many recipients with Mustache templates.

## How templates work

Email templates let you send flexible, custom emails to many different recipients in a single request. MailChannels currently
supports [Mustache](https://mustache.github.io/mustache.5.html) templates.

### Enabling templates

Two changes turn a regular send into a template send.

**1. Mark the content part as a template.**

<CodeGroup>
  ```json cURL theme={null}
  "content": [
    {
      "type": "text/plain",
      "value": "Hello {{name}}",
      "template_type": "mustache"
    }
  ]
  ```

  ```javascript Node.js theme={null}
  template: { type: "mustache" },
  text: "Hello {{name}}",
  ```

  ```python Python theme={null}
  "content": [
      {
          "type": "text/plain",
          "value": "Hello {{name}}",
          "template_type": "mustache",
      },
  ],
  ```

  ```php PHP theme={null}
  'content' => [
      [
          'type'          => 'text/plain',
          'value'         => 'Hello {{name}}',
          'template_type' => 'mustache',
      ],
  ],
  ```
</CodeGroup>

<Warning>
  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.
</Warning>

**2. Supply per-recipient data.** Add per-recipient template data to each personalization.
The keys must match the placeholders in your template:

<CodeGroup>
  ```json cURL theme={null}
  "personalizations": [
    { "to": [{ "email": "alice@example.com" }], "dynamic_template_data": { "name": "Alice" } },
    { "to": [{ "email": "bob@example.com" }],   "dynamic_template_data": { "name": "Bob"   } }
  ]
  ```

  ```javascript Node.js theme={null}
  personalizations: [
    { to: [{ email: "alice@example.com" }], template: { data: { name: "Alice" } } },
    { to: [{ email: "bob@example.com" }],   template: { data: { name: "Bob"   } } },
  ],
  ```

  ```python Python theme={null}
  "personalizations": [
      {"to": [{"email": "alice@example.com"}], "dynamic_template_data": {"name": "Alice"}},
      {"to": [{"email": "bob@example.com"}],   "dynamic_template_data": {"name": "Bob"}},
  ],
  ```

  ```php PHP theme={null}
  'personalizations' => [
      ['to' => [['email' => 'alice@example.com']], 'dynamic_template_data' => ['name' => 'Alice']],
      ['to' => [['email' => 'bob@example.com']],   'dynamic_template_data' => ['name' => 'Bob']],
  ],
  ```
</CodeGroup>

<Note>
  Placeholders without a matching key in `dynamic_template_data` render as empty strings.
</Note>

### Full example

<CodeGroup>
  ```bash cURL theme={null}
  #!/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
  ```

  ```javascript Node.js theme={null}
  import { MailChannels } from "mailchannels-sdk";

  const { MAILCHANNELS_API_KEY, FROM_EMAIL, TO_EMAIL_1, TO_EMAIL_2 } = process.env;
  if (!MAILCHANNELS_API_KEY) throw new Error("Set MAILCHANNELS_API_KEY before running");
  if (!FROM_EMAIL) throw new Error("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)");
  if (!TO_EMAIL_1) throw new Error("Set TO_EMAIL_1");
  if (!TO_EMAIL_2) throw new Error("Set TO_EMAIL_2");

  const mailchannels = new MailChannels(MAILCHANNELS_API_KEY);

  const { data, error } = await mailchannels.emails.send({
    from: FROM_EMAIL,
    subject: "Hello from MailChannels",
    text: "Hello {{name}}",
    template: { type: "mustache" },
    personalizations: [
      { to: [{ email: TO_EMAIL_1 }], template: { data: { name: "Alice" } } },
      { to: [{ email: TO_EMAIL_2 }], template: { data: { name: "Bob"   } } },
    ],
  });

  if (error) {
    console.error("Send failed:", error);
    process.exit(1);
  }
  console.log(data);
  ```

  ```python Python theme={null}
  import os
  import sys

  import mailchannels

  if not os.environ.get("MAILCHANNELS_API_KEY"):
      sys.exit("Set MAILCHANNELS_API_KEY before running")

  from_email = os.environ.get("FROM_EMAIL")
  to_email_1 = os.environ.get("TO_EMAIL_1")
  to_email_2 = os.environ.get("TO_EMAIL_2")
  if not from_email:
      sys.exit("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)")
  if not to_email_1:
      sys.exit("Set TO_EMAIL_1")
  if not to_email_2:
      sys.exit("Set TO_EMAIL_2")

  response = mailchannels.Emails.send(
      {
          "from": {"email": from_email},
          "subject": "Hello from MailChannels",
          "content": [
              {
                  "type": "text/plain",
                  "value": "Hello {{name}}",
                  "template_type": "mustache",
              },
          ],
          "personalizations": [
              {"to": [{"email": to_email_1}], "dynamic_template_data": {"name": "Alice"}},
              {"to": [{"email": to_email_2}], "dynamic_template_data": {"name": "Bob"}},
          ],
      }
  )

  print(response)
  ```

  ```php PHP theme={null}
  <?php

  require_once __DIR__ . '/vendor/autoload.php';

  use MailChannels\Client;

  $api_key    = getenv('MAILCHANNELS_API_KEY') or exit("Error: MAILCHANNELS_API_KEY is not set\n");
  $from_email = getenv('FROM_EMAIL') or exit("Error: FROM_EMAIL is not set\n");
  $to_email_1 = getenv('TO_EMAIL_1') or exit("Error: TO_EMAIL_1 is not set\n");
  $to_email_2 = getenv('TO_EMAIL_2') or exit("Error: TO_EMAIL_2 is not set\n");

  $client = new Client(apiKey: $api_key);

  $response = $client->emails->send([
      'from'    => ['email' => $from_email],
      'subject' => 'Hello from MailChannels',
      'content' => [
          [
              'type'          => 'text/plain',
              'value'         => 'Hello {{name}}',
              'template_type' => 'mustache',
          ],
      ],
      'personalizations' => [
          ['to' => [['email' => $to_email_1]], 'dynamic_template_data' => ['name' => 'Alice']],
          ['to' => [['email' => $to_email_2]], 'dynamic_template_data' => ['name' => 'Bob']],
      ],
  ]);

  print_r($response->toArray());
  ```
</CodeGroup>

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](https://mustache.github.io/mustache.5.html) for the complete reference; features not in the
[supported features](#supported-features) list at the bottom of this page are not available.

### Nested fields

Use `.` to access nested fields.

**Data**

```json theme={null}
"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**

```json theme={null}
"dynamic_template_data": {
  "items": [
    { "name": "Standard plan", "price": "9.99" },
    { "name": "Add-on seat",   "price": "4.00" }
  ]
}
```

**Template**

```html theme={null}
<ul>
{{#items}}<li>{{name}}: ${{price}}</li>
{{/items}}</ul>
```

**Result**

```html theme={null}
<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**

```json theme={null}
"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>
```

<Warning>
  Only emit raw HTML for content you control. Substituting recipient-supplied values without escaping is a common HTML
  injection vector.
</Warning>

### 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](https://mustache.github.io/mustache.5.html):

* Variables
* Dotted names
* Implicit iterator
* Non-empty lists
* Non-false values
* Inverted sections
* Comments
* Set delimiter
