Email Merge Tags

Overview

Email templates in the UniSignIn block editor are personalised with merge tags. A merge tag is a value, a condition or a loop between double braces, such as {{profile.first_name}}. Tags work in text blocks, button and image URLs, the subject line, the preheader, sub-templates, Raw HTML blocks and link parameters.

This page is the full reference. For the editor itself, see Email Templates.

Values

<p>Hi {{profile.first_name}},</p>
<p>Your order {{order.number}} has {{order.items.size}} items.</p>
<img src="{{order.items[0].image}}" alt="{{order.items.first.name}}">
  • Use dots to read nested values, and [0] for the first item of a list.
  • .size (or .length) gives the number of items in a list or characters in a text. .first and .last give the first and last item of a list.
  • Values are HTML-escaped. Use three braces to insert a value as raw HTML: {{{article.body_html}}}.
  • A value that is missing or empty prints nothing. The Checks tab of the editor warns you about it.
  • {{! a comment }} is left out of the email.

Fallbacks

Give a value a fallback with the default filter:

<p>Hi {{profile.first_name | default:"there"}},</p>

The fallback is used when the value is missing, empty or null. A value with a fallback is never reported as missing.

Conditions

{{#if profile.plan == "pro"}}
  <p>Thanks for being a Pro member.</p>
{{else if profile.plan == "trial"}}
  <p>Your trial ends soon.</p>
{{else}}
  <p>Upgrade to Pro today.</p>
{{/if}}

{{#unless profile.email_verified}}
  <p>Please verify your email address.</p>
{{/unless}}
OperatorMeaning
== and !=Equal and not equal. "5" equals 5.
>, >=, < and <=Compare numbers, or text alphabetically. A missing value counts as 0 against a number.
containsText contains a word, ignoring case, or a list contains an item.
and or &&Both are true.
or or ||Either is true.
notThe opposite, e.g. not profile.vip.

A value on its own is true unless it is missing, empty, 0, false or an empty list, e.g. {{#if order.discount}}. Compare with text in quotes, numbers, true, false or null.

The same conditions work in a block's Show this block when setting, without the braces: profile.plan == "pro" and profile.country != "US".

Loops

{{#each order.items}}
  <p>{{@number}}. {{name}}: {{qty}} × {{price | currency:"GBP"}}</p>
{{else}}
  <p>Your basket is empty.</p>
{{/each}}
  • Inside the loop, the fields of the current item are read directly, e.g. {{name}}. Use {{this}} for the item itself, such as a list of words. Fields outside the item, such as {{profile.first_name}}, still work.
  • Name the item with as, e.g. {{#each content_lists.top_stories as story}}…{{story.title}}…{{/each}}.
  • Show at most a number of items with limit, e.g. {{#each order.items limit 3}}. Use both as {{#each list as item limit 3}}.
  • {{else}} is shown when the list is empty or missing.
  • Looping over an object goes through its values; {{@key}} gives the key.
VariableValue
@indexPosition from 0.
@numberPosition from 1.
@firstTrue for the first item.
@lastTrue for the last item.
@keyThe key when looping over an object, otherwise the position.

With

{{#with}} reads several fields of one object without repeating its name. {{else}} is shown when the object is missing or empty.

{{#with order.shipping}}
  <p>{{name}}<br>{{street}}<br>{{city}}</p>
{{else}}
  <p>Collect in store.</p>
{{/with}}

Sub-templates

A sub-template is a named piece of HTML, set up in the editor. Include it with {{>:

{{#each order.items}}
  {{> line_item}}
{{/each}}

{{> article_card content_lists.top_stories.first}}
  • {{> name}} renders the sub-template with the current data, e.g. the current item of a loop.
  • {{> name path}} renders it with the value at path.
  • Sub-template names use letters, digits, _ and -, starting with a letter.

The Data list block writes the loop for you. In sub-templates and Raw HTML blocks, {{theme.accent}}, {{theme.on_accent}}, {{theme.text}}, {{theme.muted}}, {{theme.line}}, {{theme.tint}}, {{theme.card}}, {{theme.page}}, {{theme.font}} and {{theme.radius}} insert the email's design settings.

Filters

A filter changes a value. Add it after a |, with arguments after a :, separated by commas. Filters can be chained and run from left to right:

{{profile.city | default:"your area" | upper}}
{{price | times:qty | currency:"EUR"}}

Arguments are text in quotes, numbers, or other values such as qty.

Text

FilterExampleResult
default{{profile.first_name | default:"there"}}there when empty
upper{{"news" | upper}}NEWS
lower{{"NEWS" | lower}}news
capitalize{{"hello world" | capitalize}}Hello world
title{{"hello world" | title}}Hello World
truncate{{description | truncate:60}}Cut to 60 characters, ending in …. Default 80.
replace{{"a-b-c" | replace:"-"," "}}a b c
urlencode{{"a b&c" | urlencode}}a%20b%26c, for use in a URL
json{{order | json}}The value as JSON

Numbers

FilterExampleResult
number{{1234.567 | number}}1,234.57. Up to 2 decimals by default.
number{{1234.5 | number:2}}1,234.50
currency{{37.5 | currency:"GBP"}}£37.50. Default USD.
round{{3.14159 | round:2}}3.14. Default 0 decimals.
percent{{0.256 | percent:1}}25.6%. Default 0 decimals.
plus{{10 | plus:5}}15
minus{{10 | minus:5}}5
times{{12.5 | times:2}}25
divided_by{{10 | divided_by:4}}2.5. Dividing by 0 gives 0.

Currencies use the English format, e.g. €1,234.50, ¥1,235 or CHF 70.20.

Lists

FilterExampleResult
size{{order.items | size}}Number of items, or characters in a text
first{{tags | first}}The first item
last{{tags | last}}The last item
join{{tags | join:" / "}}news / sport. Default , .
plural{{count}} {{count | plural:"item","items"}}1 item, 3 items. Without the second word, s is added.

Dates

FilterExampleResult
date{{published_at | date:"MMM D, YYYY"}}Oct 8, 2026. This is also the default.
date{{published_at | date:"dddd D MMMM [at] HH:mm"}}Thursday 8 October at 09:30

Dates are read from ISO text such as 2026-10-08T09:30:00Z, or a Unix timestamp in seconds or milliseconds, and shown in UTC. Include Z or an offset in your dates.

TokenOutput
YYYY, YY2026, 26
MMMM, MMM, MM, MOctober, Oct, 10, 10
DD, D08, 8
dddd, dddThursday, Thu
HH, H24-hour clock, 09, 9
hh, h12-hour clock, 09, 9
mm, ssMinutes and seconds
A, aAM or PM, am or pm
[text]The text as written

Colours

FilterExampleResult
mix{{brand.color | mix:"#FFFFFF",0.9}}A light tint of the brand colour. 0 keeps the first colour, 1 gives the second. Default 0.5.

Data

Which values a tag can read depends on the email.

Your email templates and campaigns

DataExampleWhat it holds
profile{{profile.first_name}}The recipient's profile attributes, read when the email is sent. The field picker lists your attributes.
content_lists{{#each content_lists.top_stories}}Your content lists, read when the email is sent. Each item has title, url, image, description, author, category and published_at.
links.unsubscribe<a href="{{links.unsubscribe}}">Unsubscribes the recipient in one click, after a confirmation page. In test emails and system emails, it opens the email preferences page.
links.preferences<a href="{{links.preferences}}">The recipient's email preferences page.
campaign{{campaign.name}}The campaign sending the email: id, name and run_date, the date of the send, e.g. 2026-10-12. Empty in emails not sent by a campaign, so add a fallback such as {{campaign.name | default:"email"}}.

Tags also work in link parameters, e.g. utm_campaign set to {{campaign.name | default:"email"}} once in Email Settings tags every campaign's links with its own name.

Other keys are read from the root, e.g. {{order.number}}. In the editor, they come from the sample data under Event payload (JSON), used for the preview and test sends. Don't use a top-level key called profile in sample data; profile attributes replace it.

System emails

System emails use the same syntax in the block editor. Their data is listed in the field picker, and fields marked as required must be in the email.

DataExampleWhat it holds
user{{user.first_name}}The reader's email, first_name, last_name, full_name and display_name.
app{{app.display_name}}The project, e.g. display_name, domain, logo, privacyUrl and tosUrl.
brand{{brand.color}}The project's email branding: color, button_text, background, logo, footer_text and footer_links.
signin{{signin.temp_password}}Sign-in values, such as the passcode, the reset or verification link and subscription_manage_url.
subscription{{subscription.renew_at}}The plan in subscription and renewal emails.
invoice{{#each invoice.items}}The order, items and totals in the Invoice email.
countdown{{countdown.header}}The event in the Countdown Reminder email.

Example

A newsletter row with a fallback, a condition, a loop and filters:

<p>Hi {{profile.first_name | default:"there"}},</p>

{{#if content_lists.top_stories.size > 0}}
  <h2>Top stories</h2>
  {{#each content_lists.top_stories as story limit 5}}
    <p>
      <a href="{{story.url}}">{{story.title}}</a><br>
      {{story.description | truncate:120}} · {{story.published_at | date:"MMM D"}}
    </p>
  {{/each}}
{{else}}
  <p>No new stories this week.</p>
{{/if}}

<p><a href="{{links.preferences}}">Email preferences</a></p>

Stay ahead in first-party data

Identity, consent, audience, and subscription insights for publishers. A few emails a month, no spam.

By subscribing you agree to our privacy policy.