Emails
Personalization
Names, event data, line items, and fallbacks.
Merge tags fill in details for each recipient at send time. The same tags work in the editor, campaigns, Journeys, and transactional sends.
Quick reference
| You want | Write | Available in |
|---|---|---|
| A contact field | {{ contact.first_name }} | Everywhere |
| A fallback for empty fields | {{ contact.first_name | "there" }} | Everywhere |
| Data from the triggering event | {{ data.order_id }} | Event-triggered Journeys, transactional sends |
| A list of items | Repeat block over data.items | Event-triggered Journeys, transactional sends |
| Different content per person | Conditional content | Everywhere |
Contact fields
Hi {{ contact.first_name | "there" }},
Your plan: {{ contact.plan }}Use the field key from Data → Fields (first_name), not its display label. Confirm custom fields before using them in authored email; unconfirmed fields are not substituted. Add a fallback to every greeting so no one gets "Hi ,".
Event data
When a Journey starts from an event, or your backend sends a transactional email, the event's properties are available as data:
Order {{ data.order_id }} is {{ data.status }}.
Total: {{ data.total }}Four rules that save debugging time:
- Snapshot at entry. Journey emails use the event data from the moment the person entered. A later event does not change it.
- Audience and field triggers have no `data`. Use
contact.*there. - Declare keys in an [event catalog](../api/events.md#event-catalog-as-code). For Journeys whose entries all use declared events, saving rejects unknown
data.*keys against every triggering event. Legacy projects, mixed entry types, undeclared triggers and standalone transactional emails do not have this catalog check. Missing runtime values still need fallbacks and testing. - Keys are case-sensitive.
orderIdandorder_idare different.
For transactional sends, {{ order_id }} without data. also works. In Journeys, use data. so it is clear where the value comes from.
Line items
To list products on a receipt or articles in a digest, pass an array of objects and add a Repeat block bound to it. Inside the block, use the item's keys.
{
"dataVariables": {
"order_id": "ord_1842",
"items": [
{ "name": "Pro plan", "amount": "$29" },
{ "name": "Usage overage", "amount": "$4" }
]
}
}An empty or missing array renders nothing. If the email must still make sense without items, add a sentence outside the Repeat block.
Links
Buttons and links can contain merge tags:
https://app.example.com/orders/{{ data.order_id }}The result must be a real https URL. Placeholder links fail the confirmation check.
Preview with a real contact
Under Test data, select a contact and enter Event data (JSON) as an object to check the rendered preview. Send test uses that context while delivering to your test address. The authoring canvas retains merge tags. Neither preview nor test data changes the contact or the saved email.
The API accepts contactId and dataVariables on saved and draft previews and test sends. missingValues lists missing visible values without a fallback; unresolvedVariables lists tags still present after substitution. Check both. Without a chosen test context, preview uses samples rather than a recipient's actual data.