# Temple template language

> Temple fills variables, defaults and conditionals into email subjects, HTML and text at send time. Syntax, truthiness, escaping and where Temple runs.

Temple is Emailit's small template language for subject lines, HTML and plain text. It isn't Liquid or Handlebars: it supports variables, nested paths, default values and `if`/`else` blocks, and nothing else. Templates store placeholders as you write them, and Temple fills them in when an email is sent through the API or an automation.

## Syntax at a glance

```text
{{first_name}}                      Variable
{{user.name}}   {{items.0.sku}}     Nested property and list item
{{first_name|"there"}}              Default when the value is missing or null
{{#if plan}} … {{else}} … {{/if}}   Conditional, with an optional else
```

Temple has no loops, filters, helpers, partials or custom functions. The only operator is the `|` default.

## Variables

```text
Hello {{first_name}}
```

- Temple replaces `{{first_name}}` with the `first_name` value you provide. Spaces inside the braces are ignored, so `{{ first_name }}` works too.
- Names are case-sensitive: `{{First_Name}}` doesn't match `first_name`.
- A missing or `null` value becomes an empty string. Unknown placeholders disappear instead of showing up in the email.
- Values are converted to text. Numbers and booleans appear as written (`42`, `true`), and lists are joined with commas (`["a","b"]` becomes `a,b`). An object renders as `[object Object]`, so point at one of its fields instead.

## Nested properties and list items

Use dots to reach into objects, and numbers for list positions, starting at 0:

```text
{{user.name}}
{{order.items.0.sku}}
```

```json
{
  "user": { "name": "Ada" },
  "order": { "items": [{ "sku": "A1" }, { "sku": "B7" }] }
}
```

Because the dot separates path segments, a key that itself contains a dot can't be reached. Use keys without dots.

## Default values

Add `|` and a fallback to use when the value is missing or `null`:

```text
Hi {{first_name|"there"}},
Your company: {{company|'Not set'}}
```

Double quotes, single quotes or no quotes all work. The default isn't used for an empty string, `0` or `false`; those render as empty, `0` and `false`. A default can't contain the `}` character.

## Conditionals

```text
{{#if plan}}
Thanks for being on the {{plan}} plan.
{{else}}
You're on the free plan. Upgrade anytime from your dashboard.
{{/if}}
```

- `{{else}}` is optional.
- A condition is **false** when the value is missing, `null`, `false`, `0`, an empty string `""` or an empty list `[]`. Anything else is **true**, including the string `"0"`, the string `"false"` and an empty object.
- A condition is a single variable path, such as `plan` or `user.is_admin`. There's no `==`, `and`, `or`, `not` or `unless`. To branch on a value, compute a boolean in your code and pass it, for example `"is_pro": true`.
- Write `{{else}}` and `{{/if}}` exactly as shown, with no spaces inside the braces.
- Temple processes conditionals first, then variables.

> **Don't nest conditionals:** Temple ends a block at the nearest `{{/if}}`, so an inner block closes the outer one early and the output is wrong. Use blocks one after another instead, and pass a combined flag such as `"pro_and_annual": true` when you need both conditions.

## Escaping and special characters

**Values aren't HTML-escaped.** Temple inserts values exactly as you pass them. A value such as `Tom & Jerry` or `<b>Ada</b>` goes into the HTML unchanged, so escape any user-supplied text in your code before you pass it. The same variables fill the subject, HTML and text, so an escaped value such as `Tom &amp; Jerry` also appears that way in the subject and text. If that matters, pass a separate, escaped variable for the HTML.

This also means you can pass ready-made HTML, such as a table of order lines that your code renders, as a single variable.

**There's no escape syntax for double braces.** Temple treats anything between `{{` and `}}` as a placeholder and removes it if there's no value. Single braces, such as those in CSS, are unaffected. Values are inserted once and not parsed again, so to show literal double braces, put them in a variable:

```json
{
  "html": "<p>Write {{example}} in your template to show the first name.</p>",
  "variables": { "example": "{{first_name}}" }
}
```

## Where Temple runs

| Where | Temple runs? | What you can use |
| --- | --- | --- |
| [Email API](/docs/email-api/send-email/#send-with-a-template) with a `template` | Always | The `variables` you pass |
| Email API with inline `subject`, `html` or `text` | When `variables` has at least one key | The `variables` you pass |
| [Automations](/docs/automations/steps/), **Send email** step | On every send | Contact fields, `contact`, `payload` and `meta`. See [Automation emails](#automation-emails). |
| [Campaigns](/docs/campaigns/merge-tags/) and campaign test sends | No | A fixed set of campaign merge tags. See [Campaigns](#campaigns). |
| [SMTP relay](/docs/smtp/) | No | Nothing. The message is sent as you built it. |
| Dashboard editors and previews | No | Editors insert placeholders, and previews show them unrendered. |

### API emails

Pass a template alias or `tem_` ID, and a `variables` object. Fields you send in the request (`subject`, `html`, `text`) replace the template's, then Temple renders the subject, HTML and text.

```json
{
  "from": "Acme <hello@acme.com>",
  "to": "ada@example.com",
  "template": "welcome-email",
  "variables": {
    "first_name": "Ada",
    "plan": "Pro",
    "activation_url": "https://acme.com/activate?token=8f3k2",
    "cf": { "company": "Analytical Engines Ltd" }
  }
}
```

The dashboard editors insert `{{cf.<key>}}` for contact custom fields. In API sends, nothing is looked up from your contacts, so provide those values yourself under `cf`, as in the example. The same goes for `{{unsubscribe_url}}`: pass your own unsubscribe link if the template uses it.

You can also send inline content with `variables` and no template. See [Send an email](/docs/email-api/send-email/).

### Automation emails

On every send, the **Send email** step renders the subject, HTML and text with Temple, whether they come from the template or from overrides on the step.

**Contact automations** put the contact's fields at the top level, so these work:

- `{{email}}`, `{{first_name}}` and `{{last_name}}`
- `{{custom_fields.<key>}}` for custom fields, for example `{{custom_fields.plan}}`. The campaign form `{{cf.plan}}` doesn't work here.
- `{{contact.*}}`, the same contact as an object, for example `{{contact.first_name}}`

**All automations** also get `{{payload.*}}`, the data of the event that triggered the run, and `{{meta.*}}`, the run's metadata. **Email** and **event** automations don't have a contact at the top level, so use `{{payload.*}}` or set the recipient and subject on the step.

`{{unsubscribe_url}}` isn't filled in automation emails.

### Campaigns

Campaigns and campaign test sends don't use Temple. They replace only these merge tags with the recipient's details:

- `{{first_name}}`, `{{last_name}}` and `{{email}}`
- `{{unsubscribe_url}}`, the recipient's unsubscribe link
- `{{cf.<key>}}`, a contact custom field, for example `{{cf.plan}}`

Write them without spaces inside the braces. The tag names aren't case-sensitive, but custom field keys must match exactly. Defaults (`|`) and `{{#if}}` blocks aren't processed, and any other `{{…}}` text stays in the email as written. See [Campaign merge tags](/docs/campaigns/merge-tags/).

### SMTP

The SMTP relay accepts a finished message. There's no template lookup and no Temple pass, so build the final HTML before you send, or use the API or an automation if you need variables.

## Check templates before you publish

Emailit doesn't reject a template or a send because of broken Temple syntax. Mistakes usually show up as missing text or leftover braces in the delivered email. Before you publish:

- Check that every `{{#if …}}` has a matching `{{/if}}`, and every `{{` has a closing `}}`.
- Send the draft version to yourself by its `tem_` ID with realistic `variables`, including missing and empty values, to see both sides of each condition. See [Template versions](/docs/templates/versions/).

## Examples

A greeting with a fallback:

```text
Hi {{first_name|"there"}},
```

A block that depends on the plan:

```text
{{#if plan}}
Your plan: {{plan}}
{{else}}
Upgrade anytime from your dashboard.
{{/if}}
```

A subject with a nested value:

```text
Order {{order.number}} has shipped, {{user.first_name|"friend"}}
```

## Related

- [Send with a template](/docs/email-api/send-email/#send-with-a-template)
- [Create and edit templates](/docs/templates/editors/)
- [Campaign merge tags](/docs/campaigns/merge-tags/)
- [Automation steps](/docs/automations/steps/)

---
Source: https://emailit.com/docs/templates/temple/
