Skip to main content

Merge tags

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

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​

TagBecomes
{{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​

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:

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:

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 {{…}}.