> Lettrove docs 1.x · https://docs.lettrove.com/docs/editor/merge-tags

# Merge tags

Your users personalise a design by typing **merge tags** where a person's details go:

```text
Hi {{contact.first_name | "there"}}, your order from {{contact.company}} has shipped.
```

When you export, you give the values; every tag is replaced, in the subject, the preheader, the
HTML and the plain text.

## The tags

| Tag | Becomes |
|---|---|
| `{{contact.<key>}}` | The value you pass for `<key>`. Any key: `first_name`, `company`, `plan`, `order_id`… |
| `{{contact.<key> \| "fallback"}}` | The same, or the fallback when the value is missing or blank |
| `{{unsubscribe_url}}` | Your unsubscribe link |
| `{{preferences_url}}` | Your preferences link (your unsubscribe link when you give none) |

Keys are whatever your product knows about a person. Lettrove has no fixed list: the tags in a design
are the ones your users typed.

## Filling them at export

```ts
const email = await editor.exportHtml({
  merge: {
    contact: { first_name: 'Ada', company: 'Analytical Engines' },
    unsubscribeUrl: 'https://acme.com/unsubscribe/7f3a',
    preferencesUrl: 'https://acme.com/preferences/7f3a', // optional
  },
});

email.subject; // 'Your order has shipped, Ada'
email.html;    // '…Hi Ada, your order from Analytical Engines has shipped…'
email.mergeTags; // [] — nothing left to fill
```

The same `merge` works on `exportPlainText`, the file exports, and on your server:

```ts
const email = await lettrove.designs.export(designId, {
  user: { id: userId },
  merge: { contact: { first_name: 'Ada' }, unsubscribeUrl },
});
```

Values are written into the HTML safely: a value like `<b>Ada</b>` appears as that text, never as
markup.

When you pass `contact`, a tag whose value you did not give takes its fallback, or becomes empty if
it has none. So `Hi {{contact.first_name}},` with no `first_name` reads `Hi ,` — encourage your users
to give tags a fallback (`{{contact.first_name | "there"}}`).

## Leaving them for your email service

Many email services (SendGrid, Mailchimp, Customer.io…) fill their own tags per recipient. Export
**without** `merge` and every tag stays exactly as written, for your service to replace:

```ts
const { html, mergeTags } = await editor.exportHtml();
mergeTags; // ['{{contact.first_name | "there"}}', '{{unsubscribe_url}}']
```

`mergeTags` lists every tag still in the email, exactly as written, so your code can map them to
your service's own syntax before sending.

A link you do not give stays as written too, never blank: export with `contact` but without
`unsubscribeUrl`, and `{{unsubscribe_url}}` is left for your service.

## Before you send

Check `mergeTags` is empty, or that your email service fills every tag in it. A recipient must never
see a raw `{{…}}`.
