Skip to main content
Personalized emails outperform generic ones. Recipients are more likely to open, read, and act on emails that address them by name, reference their specific activity, and show content relevant to their situation. Lettr’s template language provides merge tags, conditionals, loops, and filters that let you build a single template that renders differently for each recipient.

Merge Tags

Merge tags are placeholders in your template that get replaced with recipient-specific data at send time. They use double curly braces.

Basic Substitution

Pass the substitution data in your API request:

Default Values

Always provide defaults for personalization fields. An email that reads “Hello ,” because the name field is missing looks broken.
Never assume all substitution data will be present. Missing fields render as empty strings by default, which creates gaps in your content. Use the or keyword on every variable that appears in visible text.

HTML Escaping

By default, merge tags are HTML-escaped to prevent cross-site scripting. If you need to render trusted HTML content, use triple curly braces:
Only use triple curly braces ({{{ }}}) with content you control — never with user-submitted data. Unescaped content can inject arbitrary HTML into your emails.

Conditional Content

Show different content to different recipients based on their attributes, plan level, activity, or any other data you pass.

Basic Conditionals

Multi-Branch Conditionals

Use {{elseif}} for more than two branches:

Practical Use Cases for Conditionals


Dynamic Loops

Use {{each}} to iterate over arrays of data — order items, activity logs, recommendations, or any list.

Order Items

With this substitution data:

Combining Loops with Conditionals


Per-Recipient Personalization at Scale

When sending to multiple recipients in a single API call, pass individual substitution data for each recipient:
Batch sending with per-recipient substitution data is the most efficient way to send personalized emails at scale. One API call with 200 recipients and individual data is much faster than 200 separate calls.

Personalization Strategy

What to Personalize

Not everything needs personalization. Focus on elements that make the email feel relevant and useful.

What Not to Personalize


Testing Personalized Templates

Preview with Sample Data

Before sending, test your template with different substitution data to verify all branches render correctly.
Pay special attention to empty states. What does your template look like when items is an empty array? When name is missing? When a conditional branch has no matching case? Every possible state should render cleanly.

Common Mistakes

Forgetting the or keyword results in empty strings when data is missing. “Hello ,” or “Your plan includes emails per month” looks broken. Always add fallbacks: {{ name or 'there' }}.
Using “Hey !” in every subject line has diminishing returns. Recipients learn to ignore it, and some find it off-putting. Save name personalization for the email body and keep subject lines focused on the content.
If you have 4 plan tiers, you need to test all 4 renderings plus the else fallback. A broken conditional in a branch you didn’t test will surprise you in production.
Triple curly braces ({{{ }}}) skip HTML escaping. If user-submitted data contains HTML tags or scripts, it renders raw in the email. Only use triple braces with system-generated, trusted content.
Testing always with name: 'Test User' and plan: 'pro' misses edge cases. Vary your test data to cover missing fields, empty arrays, long strings, and unusual characters.

Template Language

Full reference for merge tags, conditionals, loops, and filters

Templates

Build and manage email templates with the Topol editor

Batch Sending

Send to multiple recipients with per-recipient data

Template Versions

Manage and publish template versions