Personalizing WhatsApp Broadcasts with Variables
Give every recipient their own name, order number or city — with a safe fallback when a value is missing, and a clear picture of what can't vary.
Personalization is binding each numbered placeholder in an approved WhatsApp template — {{1}}, {{2}}, {{3}} — to a value that differs per recipient, so one broadcast produces thousands of individually-addressed messages rather than one message sent thousands of times.
The placeholders are not a ConvoFly invention. WhatsApp templates must be submitted to Meta for approval before you can send them, and variables are the only part of an approved template you are allowed to change afterwards (Meta, sending message templates). Everything else — the wording, the layout, the buttons — is fixed at approval. So the number of variables you ask for when you submit a template decides how personal it can ever be.
This guide covers what each placeholder can be filled from, why a fallback is compulsory, which parts of a message can and cannot vary per person, and how to check the result before spending money on a send.
The three sources a variable can pull from
Click the dropdown beside any {{n}} in the composer and you get three kinds of source. That's the complete list — there is no fourth:
| Source | Value comes from | Varies per recipient? | Fallback needed? |
|---|---|---|---|
| Custom text | What you type, once | No — identical for everyone | No |
| Contact's {field} | A contact field stored in ConvoFly | Yes | Yes — required |
| Sheet column: {header} | That person's row in the connected Google Sheet | Yes | Yes — required |
Custom text is not a lesser option. A promo code, a delivery date or a store name that is the same for everyone belongs there, and mixing it with a personalized name in the same message is the normal case: {{1}} as the contact's name, {{2}} as the shared code SPRING20.
Which sources appear depends on your audience
The dropdown changes shape with the audience tab you chose, which trips people up when the option they expect isn't there:
- By tag, By segment, Re-target — Custom text plus one entry per enabled contact field, each labelled "Contact's Order Number", "Contact's City" and so on.
- Google Sheet — Custom text, then a group headed From this Google Sheet listing every detected column, then a group headed From the contact's saved details. Until you pick a sheet, that group shows "(pick a Google Sheet below to map its columns)".
- CSV list — no dropdown at all. CSV personalization is positional: the phone number is the first value on each line, then
{{1}},{{2}}in column order. The composer labels each row accordingly, so you can see which column feeds which placeholder.
That positional rule is the main practical difference between a CSV and a Google Sheet audience: a sheet lets you map named columns in any order, a CSV requires your columns to be in the order the template expects.
Fallbacks: never send a blank
WhatsApp rejects a template message whose body variable resolves to an empty value. One contact with a missing first name would otherwise mean one failed delivery — quietly, mid-broadcast, with no obvious cause.
So every non-fixed variable gets an "If blank, use…" box, and ConvoFly refuses to launch until it is filled, with an explicit message: "Variable {{1}} is personalized — add a fallback for contacts missing that attribute", or for a sheet source, "add a fallback for rows where that sheet cell is blank."
The fallback fires in two situations, not one: when the contact has no value at all, and when the value exists but is blank after trimming spaces. A cell containing three spaces counts as empty, which is exactly what you want from real-world data.
Choosing good fallbacks is a copywriting job, not a technical one. The test is whether the sentence still reads naturally with the fallback substituted in:
| Sentence | Weak fallback | Better fallback |
|---|---|---|
| Hi {{1}}, your order is on its way | N/A → "Hi N/A," | there → "Hi there," |
| Your order {{1}} has shipped | Unknown | Rephrase the template so the number isn't optional |
| Great news for {{1}} shoppers | (blank not allowed) | your city → "Great news for your city shoppers" |
| {{1}}, your {{2}} points expire soon | 0 → "your 0 points expire soon" | Exclude those contacts with a segment instead |
The last row is the important lesson: sometimes the right answer is not a better fallback but a better audience. If a variable is missing for a big share of your list, filter those people out with an is set condition in a segment and send them a different message. A fallback should be a graceful edge case, not a way of papering over data you don't have.
Which fields you can personalize with
There is no fixed catalogue of built-in variables. Every field in the dropdown is one you or an integration created, under Settings → Contact Attributes. Two integrations populate it for you:
- Connecting Shopify creates order-related fields automatically, including Order Number, Order ID, Order Total, Currency, Payment Status, Fulfillment Status, Discount Total, Customer Name, Email, Tracking Number, Tracking Link, Checkout URL and Order Status URL. These are what make order and shipping messages possible with no manual data entry.
- Connecting WhatsApp creates the Click-to-WhatsApp ad fields — Facebook Ad ID, Facebook Ad Link, Facebook Ad Headline, Facebook Ad Text and a Facebook Ad Date — so you can reference the ad someone came from.
Note that Shopify supplies a single Customer Name field, not separate first and last names. If you want first-name-only greetings, create your own Text field and populate it — either with an Update Customer Attribute step in the automation builder, or by pushing values from your own system with the account API key in Settings. The same is true of a city field: it exists only if something in your setup writes it.
Everything you create becomes available in both places at once — as a personalization source here, and as a filter in the segment builder. Adding one field improves your targeting and your copy simultaneously, which is the strongest argument for collecting a little more structured data than you think you need.
What cannot be personalized per recipient
Being clear about this saves an afternoon:
- Only body variables vary per person. The mapping rows are generated from the placeholders found in the template's body text.
- Header media is one file for the whole broadcast. You can attach an image, video or document, but every recipient gets the same one. You cannot send each customer a picture of the product they bought in a single broadcast.
- Button parameters are fixed for the broadcast. A dynamic-URL button takes one value that applies to everyone, so per-customer order-tracking links are not something a single broadcast can do. See adding media, buttons and offers.
- A header text variable isn't offered. Personalization applies to the message body.
If you genuinely need a per-customer link or image, the route is an automation or drip step triggered per order rather than one broadcast to a list — a different tool for a different job.
How to personalize a broadcast, step by step
- Pick your audience first. The available sources depend on it, so choosing the tag, segment or sheet before touching the variables saves re-work.
- Choose your approved template. The composer scans its body and creates one mapping row per placeholder it finds.
- For each
{{n}}, pick a source: Custom text, a contact field, or a sheet column. - Fill the "If blank, use…" box for every non-fixed source. The launch button stays blocked until you do.
- Click Preview recipients to confirm the count, then send yourself a test message and read it on a real phone.
- Launch. Values are resolved for each recipient at the moment their message is sent, not when you clicked the button.
When values are resolved — and why it matters
Personalization is late-bound. Each recipient's values are read at the moment their individual message goes out, which has real consequences on longer sends:
- Broadcasts — resolved per recipient during the send. Because sending paces at roughly 120 messages a minute, a 10,000-person list takes over an hour, and someone whose data changes at minute 40 gets the newer value.
- Drip campaigns — each step personalizes independently when that step actually sends, days or weeks after enrolment. A customer's status field can legitimately differ between step 1 and step 4.
- Google Sheet audiences — for a drip using sheet columns, the sheet is re-read at send time, so a corrected cell is picked up by later steps.
- CSV uploads — the exception. The values are captured from the file at upload and stay fixed for that campaign.
Personalization and cost
Personalization does not change what you pay. Marketing templates are charged on delivery regardless of how many variables they carry, and Meta's rates vary by country (Meta, WhatsApp pricing). What personalization changes is the return: a message that names the customer's actual order is more likely to earn a reply, and a reply opens a 24-hour customer service window in which your non-template replies are free. The cheapest follow-up conversation is the one your first message earned.
It also protects your standing. Irrelevant, generic broadcasts attract blocks, and blocks affect the quality rating that governs whether you climb from the starting 250-customer daily limit toward 2,000 and beyond (Meta, WhatsApp messaging limits). Personalization is quality-rating hygiene as much as it is copywriting.
Getting variables into your template in the first place
Because templates are approved before use, you have to ask for the placeholders up front. Two habits help:
- Include a sample value for every variable when you submit. Meta's template guidelines require examples, and templates are commonly rejected for vague or missing ones.
- Don't start or end the body with a variable, and don't put two next to each other. A template whose body is almost entirely placeholders reads as a blank form to a reviewer.
Approval turnaround is usually quick in practice, but Meta does not publish a guaranteed time — so build templates a few days before the campaign rather than the morning of it.
Five checks before you launch
- Every non-fixed variable has a fallback that reads naturally in the sentence.
- You have checked how many contacts actually have each field — an is set condition in a segment tells you in seconds.
- The test send was read on a phone, not in the browser preview alone.
- Nothing in the message implies a per-person image or link that a broadcast cannot deliver.
- Stored values are tidy — trailing spaces, ALL CAPS names and "null" strings all survive into the message exactly as written. Personalization is only ever as good as the data behind it.
Frequently asked questions
What happens if a contact is missing a personalization value?
The fallback you set for that variable is used. WhatsApp rejects a template whose body variable resolves to empty, so ConvoFly requires a fallback on every contact-field and sheet-column variable and blocks launch until one is set. The fallback also fires when a value is present but blank after trimming spaces.
Can different recipients get different values in the same broadcast?
Yes, in the message body. Each recipient's values are resolved at the moment their own message sends — not when you press launch — so on a large send paced at roughly 120 messages a minute, data that changes mid-send is reflected in later messages.
Can I personalize the image or the button link per customer?
No. Only body variables vary per recipient. Header media is a single file for the whole broadcast and button parameters are one fixed value, so per-customer tracking links need an automation or drip step triggered per order rather than one broadcast.
Is there a built-in first_name variable?
No. Every personalization field is one you or an integration created. Connecting Shopify auto-creates 14 order-related fields including Order Number, Order Total and a single Customer Name — not separate first and last names. For first-name greetings, create a Text field in Settings → Contact Attributes and populate it.
Does personalization work with CSV uploads?
Yes, but positionally rather than by name: the phone number is the first value on each line, then {{1}}, {{2}} follow in column order, with no source dropdown. A Google Sheet audience instead lets you map named columns to any placeholder in any order.