MCP

Personalization variables

The names you may put in a campaign subject or body. Manyreach calls them variables (the web app inserts them from a Personalize menu). This page is the vocabulary; the send sequence is sending-a-campaign and what to actually write is writing-emails. How deeply to personalize — merge fields are only rung 1 of a four-rung ladder, and depth is an economics decision — is the playbook's Personalization levels and Economics of personalization.

The vocabulary is a closed list, not a rule you can apply. A variable is not the upper-cased prospect field: custom1 is written {{CUSTOM_1}} — with an underscore the field does not have — and plausible names like {{COMPANY_NAME}}, {{WEBSITE}} and {{SENDER_NAME}} are rejected even though {{COMPANY}}, {{DOMAIN}} and {{SENDER_FIRSTNAME}} are valid. Take names from the table below; anything invented fails at start with a 422.

Write them with double braces, no inner spaces, in the exact case shown: {{FIRST_NAME}}. They work in the subject and the body, on the campaign itself and on every followup. Nothing is checked at create or patch — only campaign/start_by_id validates them.

The confirmed variables

variable fills in from
{{FIRST_NAME}} first name prospect firstName
{{LAST_NAME}} last name prospect lastName
{{FULL_NAME}} full name no single prospect field holds this
{{EMAIL}} email address prospect email
{{COMPANY}} company name prospect company
{{COMPANY_SIZE}} employee count or size band prospect companySize
{{INDUSTRY}} industry prospect industry
{{JOB_POSITION}} job title prospect jobPosition
{{CITY}} city prospect city
{{STATE}} state or province prospect state
{{COUNTRY}} country prospect country
{{LOCATION}} location or address prospect location
{{PHONE}} phone number prospect phone
{{DOMAIN}} company domain prospect domain
{{ICEBREAKER}} the prepared opening line prospect icebreaker
{{CUSTOM_1}}{{CUSTOM_20}} the twenty free-text fields prospect custom1custom20
{{SENDER_FIRSTNAME}} the sending account's first name sender firstName — note: no underscore before NAME
{{SENDER_SIGNATURE}} the sender's signature block sender signature
{{UNSUBSCRIBE_LINK}} a ready-made link reading "Unsubscribe"
{{UNSUBSCRIBE_URL}} the bare unsubscribe URL, for your own link text

Prospect fields: schemas/prospect.

This table is what has been verified, not a guarantee of completeness. The validator never lists its own vocabulary, so a name that is absent here is unverified rather than proven invalid — only its answer at start settles it.

Custom variables

{{CUSTOM_1}}{{CUSTOM_20}} fill custom1custom20. Two things follow:

  • The underscore belongs to the variable, not the field. custom1{{CUSTOM_1}}. {{CUSTOM1}} is rejected, and since that name exists in no casing the error offers no correction. (Published help material writes the defaults as Custom_1; that casing is rejected too, with Placeholder has to match correct case. Change {{Custom_1}} to {{CUSTOM_1}}.)
  • These slots can be renamed in the web app, and {{CUSTOM_1}}{{CUSTOM_20}} keep working regardless. A renamed slot can additionally be written by its new name — a slot renamed to ProjectName also accepts {{ProjectName}} — but no operation exposes an account's variable names, so you cannot discover them here. Use the numbered form: it is the one that holds whatever the account has been renamed to. Only use a name if your user gives you one, and if start rejects it, fall back to the number.

Names that look right and are not

{{NAME}}, {{COMPANY_NAME}}, {{JOB_TITLE}}, {{TITLE}}, {{WEBSITE}}, {{URL}}, {{PROSPECT_ID}}, {{SIGNATURE}}, {{UNSUBSCRIBE}}, {{SENDER_NAME}}, {{SENDER_FIRST_NAME}}, {{CUSTOM_0}} and {{CUSTOM_21}} are all rejected. The sender family is the sharpest trap: {{SENDER_FIRSTNAME}} is valid while {{SENDER_NAME}} is not, and the prospect's own first name does carry the underscore ({{FIRST_NAME}}) — the two families follow different conventions.

Some prospect fields have no known variable at all: every plausible name for website ({{WEBSITE}}, {{WEBSITE_URL}}, {{COMPANY_WEBSITE}}, {{URL}}), for companySocial and personalSocial, and for screenshotUrl / logoUrl / customImageUrl is rejected. Copy such a value into a custom field if the email needs it.

What the validator tells you

campaign/start_by_id answers 422 and names every offending expression in one response, grouped under Fix Subject: and Fix Body: — one round trip is enough to correct them all. There are two messages:

message meaning do this
Wrong placeholder: {{X}} no variable of that name exists, in any casing replace it with a name from the table, or delete it
Placeholder has to match correct case. Change {{x}} to {{X}}. the name exists; only the casing is wrong use the exact form the message names

Nothing was sent. Fix the copy with campaign/patch_by_id (campaign subject/body) or followup/patch_by_id (a followup's), then start again.

Fallbacks for missing data

A variable with no value on the prospect leaves a hole in the sentence. Give it a fallback with the Liquid default filter, in straight ASCII quotes:

Hi {{FIRST_NAME}}, quick question about {{COMPANY | default: "your team"}}.

A | turns the check off. Any {{ … }} expression containing a pipe is skipped by start-time validation entirely: {{ZZ_BOGUS}} is rejected, {{ZZ_BOGUS | default: "x"}} passes, and so does a mis-cased name that would otherwise have been corrected for you. Get the spelling right from the table first, then add the filter — otherwise the mistake reaches the send instead of coming back as a 422.

default: and newline_to_br are the only filters the product documents; treat any other as unverified.

Conditionals

Liquid tags carry conditional copy. Inside {% %} the variable is written bare, without braces — braces are only for output:

{% if COMPANY %}I saw {{COMPANY}} is hiring{% else %}I saw your team is hiring{% endif %}

Available: {% if %} / {% else %} / {% endif %}, {% unless %}, {% case %} / {% when %}, and {% for %}; comparisons == != > < >= <=, plus and, or, contains. String literals take " or '.

Start-time validation skips {% %} tags but still checks plain {{ … }} expressions inside them, so a name in conditional copy must still be real. Whether conditionals are honoured in the subject is unverified — the published examples are all body copy.

A/Z (A/B) variants

Alternative wordings go in a spin tag: single braces, the literal word abtest, pipe-separated variants, and a trailing pipe before the closing brace.

{abtest|quick question|{{FIRST_NAME}}, quick question|}

It works in subject and body, on a whole message or a single phrase, takes as many variants as you like, and variables nest inside it. Variants are combined at random per send — step 1's variant A does not pair with step 2's variant A. The web app's campaign report shows every combination; the API carries no per-variant breakdown (campaign/stats_by_id), so don't go looking for one. Single-brace forms are never examined by start-time validation.

When a prospect is missing a value

deactivateIfMissingPlaceholder on the campaign decides what happens to a prospect with no value for a variable the copy needs:

  • true — the prospect is deactivated instead of mailed, and carries sendingStatus: MissingPlaceholder (schemas/prospect); the web app labels that state "Missing Tag". Filling the field (prospect/patch_by_id) makes it sendable again.
  • false — the prospect is not deactivated and the send goes ahead.

Either way, a | default: fallback or an {% if %} is what keeps incomplete records in the send with copy that still reads properly.

Not a variable: spintax

Single braces with pipes — {|Hi|Hello|Hey|} — are spintax, a different mechanism that picks one option per message rather than substituting prospect data. The two compose (a variable can sit inside a spin option), and both are validated when the campaign starts. See spintax — which also covers the trap that makes a stray { or } disappear from the delivered copy.