Guide
How to QA personalization tokens and merge tags in email
A personalization token is the one part of an email that QA never sees in its final form. The copy deck shows a placeholder, the build shows a merge tag, and the test send shows one value: whatever the seed record held. The subscriber sees something else, and for some of them it is “Hi ,”, a subject line cut before the offer, or last quarter’s product name.
This guide is for email QA analysts and developers on Salesforce Marketing Cloud, Braze, Klaviyo and similar platforms: the four ways tokens fail, and a test audience that exposes them.
What a token looks like on each platform
The syntax differs, the idea does not: a marker the platform replaces at send time. The common shapes:
%%=v(@firstName)=%% # AMPscript inline value
{{ first_name }} # Handlebars or Liquid
{{customer.first_name}} # an attribute on a customer object
{{ first_name | default: 'there' }} # Liquid with a default
*|FNAME|* # Mailchimp merge tag
[firstname,fallback=there] # Campaign Monitor
Two kinds of template text sit in a build. A token renders a value: a name, a date, an amount, a link. A control block is logic and renders nothing: Liquid’s {% if %}, Handlebars’ {{#if}}, an AMPscript block in %%[ ... ]%%. The fallback lives in one or the other. In Liquid it is the default filter on the token itself. In Marketing Cloud it is usually a block that runs before the copy:
IF EMPTY(@fn) THEN SET @fn = "there" ENDIF ]%%
Hi %%=v(@fn)=%%,
The copy deck has its own placeholders: <Firstname>, [First name], or a filled-in example such as “Hi Jordan,”. Part of the job is matching each one to the build’s merge tag.
The four ways tokens fail
1. A fallback that never fires, or fires for everyone
A fallback fires only when the value is truly empty. A field holding a single space, the string NULL, or a dash from a CRM import is not empty, so the subscriber gets “Hi NULL,”. The opposite failure is quieter. The build looks up FirstName but the data extension calls the column First_Name. The lookup comes back empty for every record, and the whole audience is greeted as “there”. The test send shows “Hi there,” too, and it is easy to read that as the fallback working.
2. A value that pushes the subject line or button past the cut
The deck’s sample name is short: Jordan, Sam, Alex. Real names are not. “Jordan, your 20% off ends Friday” fits a phone inbox. The same subject for a subscriber named Maximiliano-Alessandro loses “ends Friday”, the part that mattered. Inboxes cut by width, not character count, so test with the longest real value. Buttons carry the same risk: a fixed-width button labeled “Shop your picks, %%FirstName%%” wraps to two lines or clips on a long name.
3. A token the deck defines with a value the email never fills
The deck says “Offer ends <OfferEndDate>” and gives the value as December 31, 2026. The build may leave the merge tag unmapped, so the line reads “Offer ends .”, or a developer may hardcode the date from an earlier round. The second is the hard one: the email reads fine, it is simply wrong.
4. A token whose value differs by version or language
A multi-version campaign renders the same token differently: one product name in versions 1 to 3, another in versions 4 to 6. A bilingual send needs different formats per language: $50 against 50 $, December 31, 2026 against 31 décembre 2026. The HTML is identical in every build, so the defect only exists once a value is filled in.
Why “Dear %%FirstName%%,” looks fine in the test send
The test send goes to the team’s seed list, and every seed record is complete: a first name, a last name, a points balance. Every token resolves, every fallback is skipped, and the email looks perfect. That proves the merge works for five complete records, not for an audience where some records have no first name or a name in capitals. Treat the seed list as the happy path and add records designed to fail.
How to seed a test audience that breaks tokens
Add four records to the test audience, or to the test data extension, each with one kind of bad value in every personalized field:
- Empty value. The field is blank, and on a second record it holds a single space. The first proves the fallback fires. The second shows what a dirty import does.
- Long value. A first name of 20 characters or more, a hyphenated surname, the longest product name in the catalog. This catches the subject line cut and the button that wraps.
- Accented value. Élodie, François, Zoë. A name that arrives as “Élodie” means something is not UTF-8 end to end.
- Wrong-case value. JORDAN and jordan. If the greeting reads “Dear JORDAN,”, decide whether the build fixes case.
seed-empty, , Everyday Plan
seed-space, " ", Everyday Plan
seed-long, Maximiliano-Alessandro, Harbourline Home and Garden Rewards Plan
seed-accent, Élodie, Everyday Plan
seed-case, JORDAN, Everyday Plan
Read each copy for the greeting, the subject, the pre-header and every button, and keep the records in the team’s test data for every campaign.
The title tag and pre-header carry tokens too
Tokens are not only in the body copy. The subject line is often personalized, and the <title> tag is often a copy of it, with the same merge tag inside. The hidden pre-header is the easiest place to miss a token: it is not on screen in the email, so a leftover %%FirstName%% there shows up only in the inbox row, after the subject.
Check the subject and pre-header with the long value in place. The free inbox preview shows where each inbox cuts them, measured in its own font, so you can type the subject as it renders for Maximiliano-Alessandro and see what is lost.
Per-version values
Before testing a multi-version campaign, write the token table: each token, its value, and the versions it applies to. A value with no version scope applies to all of them.
%%ProductName%%, 1-3, Everyday Plan, Forfait Quotidien
%%ProductName%%, 4-6, Premier Plan, Forfait Premier
%%OfferEndDate%%, all, December 31, 2026, 31 décembre 2026
Check each email against the row for its own version, and confirm the version from the email’s content, not its file name: a version 5 file built from the version 2 template carries version 2’s product name. For the language half of this, the bilingual EN/FR QA guide covers French number, date and currency formats.
A worked example: Harbourline HL2316
Harbourline is a fictional retailer and HL2316 is its sample campaign; the values here are illustrative. The deck’s greeting is “Hi <Firstname>,”, the subject line is “<Firstname>, your spring offer ends <OfferEndDate>”, and the offer end date is December 31, 2026 in English and 31 décembre 2026 in French.
The first test send goes to the seed list and every copy looks right. Then the team adds the four breaking records:
- The empty record reads “Hi there,” in the body, as intended, but the subject reads “, your spring offer ends December 31, 2026”: the fallback block runs in the body only, and the subject uses the raw personalization string.
- The long record’s subject loses the date on a phone.
- The French email shows “December 31, 2026”: the date was set once for all languages.
- The pre-header ends in a literal %%OfferEndDate%%, which nobody saw because it is hidden in the body.
The fixes are small: move the fallback into the subject, give the date a French value, map the pre-header token, and put the date before the name. The first test send showed none of it. Walk the real sample campaign in the free demo.
How SendLint checks tokens
SendLint checks the email against the approved assets, and tokens are part of that pass:
- Every major syntax. Marketing Cloud %%FirstName%% and AMPscript, Handlebars, Liquid (Klaviyo, Braze, Customer.io and others), Mailchimp and Campaign Monitor are recognized, and logic blocks are not read as copy.
- Leftover tokens. The Personalization Fields check lists any token still in the HTML outside comments, scripts and styles, which takes in the title tag and a hidden pre-header, and a deck-style <First Name> that leaked into the email.
- Values read from the deck and the email. A deck’s <Firstname> is matched against the name in the test send, or a merge tag against the deck’s filled-in text, and the report notes each value with the line it came from. A deck field the email fills with two values is a warning.
- Values you set. Under the Copy deck card, Personalization tokens, Add a value sets a test value or overrides the deck. Values can differ by language, and can be scoped to versions such as 1-3, 7. In a batch, each email gets the value for the version the deck says it is. A value you set that the email does not contain is flagged.
- Tokens in the deck comparison. Token values are filled in before the copy is compared with the deck, so a personalized subject line is compared as it reads. Proper Case Names flags a greeting such as “Dear JOHN”.
The Inbox Display Preview check measures the subject and pre-header the email carries and warns when the subject passes 60 characters, so run it on the copy sent to the long seed record. The deck side of all this is covered in the copy deck comparison guide.
Frequently asked questions
- How do you test personalization tokens in an email?
- Send to seed records built to break them: one with the field empty, one with a very long value, one accented, one in the wrong case. Read every place a token appears, including the subject, pre-header, title tag and buttons, against the values the copy deck defines.
- Why does my test send look right when the live send has blank names?
- Seed records are complete: every tester has a first name on file. The live audience is not. Add a seed record with the field empty and check that the fallback appears.
- What is a fallback value in a merge tag?
- The text that renders when the field has no value, such as "there" in "Hi there,". In Liquid it is the default filter; in Salesforce Marketing Cloud it is usually an AMPscript IF EMPTY block. It fires only on a truly empty value, so a field holding a space or the word NULL skips it.
- Can a personalization token change the subject line length?
- Yes. A long first name or product name can push the offer past where the inbox cuts the subject. Test it with the longest real value, not the deck's sample.
- How do you QA tokens that differ by version or language?
- Write each token's value per version and language, then check each email against its own row. A French email needs French values, such as 31 décembre 2026 and 50 $, not the English ones.
- Does SendLint find unresolved personalization tokens?
- Yes. The Personalization Fields check lists any merge tag still in the email's HTML outside comments, scripts and styles, in every major ESP syntax. It also reads each token's value from the copy deck and the email, notes them on the report, and flags a value you registered that the email does not contain.
Keep reading
- How to QA an email against the copy deck, line by lineThe shapes a deck arrives in, both directions, per version and per language, the parts everyone skips (subject line, alt text, links, tokens), what counts as different, and what to do when the deck is revised mid-campaign.
- Bilingual email QA: how EN/FR campaigns actually go wrongLanguage leaks, links that don't switch languages, lost accents, unequal legal copy, and the pass that catches each one.
- The email QA checklist: every check before you sendEvery check a bank-grade email QA pass covers, why each exists, and which ones humans consistently miss.