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 wantWriteAvailable 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 itemsRepeat block over data.itemsEvent-triggered Journeys, transactional sends
Different content per personConditional contentEverywhere

Contact fields

text
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:

text
Order {{ data.order_id }} is {{ data.status }}.
Total: {{ data.total }}

Four rules that save debugging time:

  1. Snapshot at entry. Journey emails use the event data from the moment the person entered. A later event does not change it.
  2. Audience and field triggers have no `data`. Use contact.* there.
  3. 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.
  4. Keys are case-sensitive. orderId and order_id are 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.

JSON
{
  "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.

Buttons and links can contain merge tags:

text
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.