Campaign template + variable personalization walkthrough
The campaign end-to-end guide builds the flight: audience, dry-run, launch, measurement. This guide is the creative bake — everything that happens to the message between “draft text” and “ready to launch.” Walk it once and a generic campaign body becomes a polished, channel-aware template with a tested variant set and a pre-flight score you trust. The order is load-bearing: settle the creative per channel first, then resolve variable slots, then preview coverage and list hygiene, then split variants with a holdout, and only then hand the finished body to the deliverability lab. Each step below is read-only until the final launch call, so re-run every gate on every edit. For the wizard-driven version of the same flow, see Build a blast or drip campaign with the create wizard. Everything here also runs from the API — the wizard is a client of the same endpoints.1. Framing: creative → channel → slots → locales → A/B → hygiene → pre-flight
Treat the creative side as a ladder with one rule: original beats generic, specific beats original. A recipient-specific{{first_name}} opener on the right channel outperforms a polished generic blast, and a channel-native body outperforms a reused one. The ladder this guide walks:
- Channel entries — write the body per channel instead of porting one SMS to everywhere (section 2).
- Variable slots — swap literals for merge-tags from the catalogue, and validate them (section 3).
- Locales — translate the finished template, preserving tokens and the opt-out line (section 4).
- A/B + holdout — split the polished body against alternatives with a randomized control (section 6).
- List hygiene — project what the send gates will drop and what it would have cost (section 5).
- Deliverability-lab pre-flight — score filtering risk per carrier and iterate until green (section 8).
2. Channel entries: write per channel, not once
A campaign’schannel (or its channels[] fallback chain) decides what the body can carry. Write each entry natively; the wizard’s Message step and the API’s message_template accept up to 100,000 chars, but each channel’s real budget is smaller.
SMS: one segment budget
One GSM segment is 160 chars; a U.S. carrier-grade target is under 160 so no handset sees a split. Put the hook and the call to action in the first 80 chars, and keep exactly one opt-out instruction.MMS: attach the visual
MMS carries an image or video plus a longer caption. Use it when the creative asset does the selling — a product shot beats three extra sentences. Keep the caption in SMS shape anyway; handsets that fall back from MMS to SMS get the text alone.Email: subject + visual body
Email adds asubject line and an HTML body built in the dashboard’s visual builder (or handed to the API as HTML). Run the same variable slots in the subject and body; the personalization preview validates both.
WhatsApp: approved template with numbered parameters
WhatsApp sends through pre-approved message templates whose variables are positional tokens —{{1}}, {{2}} — bound in template-parameter order, not contact-field merge-tags. Two consequences:
- Composing is: pick the approved template, then map each positional slot to a contact field or campaign variable.
{{ first_name }}-style tags and the positional{{1}}form are validated by different layers — the merge-tag validator deliberately skips numeric tokens, so both can coexist in one body without false warnings.
Hi {{1}}, your order {{2}} ships today. with slots {{1}} → first_name, {{2}} → variables.order_ref.
RCS: rich card with fallback covered
RCS carries rich cards — a title, body, media, and action buttons — so the creative unit is the card, not the line of text. Build the card, then write the SMS fallback text anyway: recipients whose handset or carrier cannot receive RCS get the SMS rendition on the same dispatch. The create wizard’s RCS sender panel reports your sender verification state; build now and launch when verified. The RCS campaign launch playbook covers verification and card shapes.Editable samples, per send
Whatever the channel, the body stays editable until launch. The patterns above are starting points — edit them per campaign, and keep the opt-out line exactly once on every carrier-gated channel.3. Variable slots: catalogue → insert → validate
Personalization tokens use{{token}} form and resolve per recipient at send time. Build them from the template-variable catalogue, not memory:
key between double braces — {{first_name}}, never {{First name}}. The three contract layers, cheapest to richest:
- The five standard contact fields —
{{first_name}},{{last_name}},{{phone}},{{email}},{{company}}. Resolver-backed; prefer them for reusable creative. {{coupon_code}}— per-recipient generated coupon, driven by the campaign’s coupon config.- Campaign variables — every key on
campaign.variablesbecomes a tag that resolves identically for every recipient:variables: { "promo_expiry": "Sunday" }renders{{promo_expiry}}as “Sunday” for everyone.
Personalization preview: catch the blank before launch
The campaign dry-run validates every{{...}} tag in the bodies it would actually send against the full contract, and reports two things:
personalization.unresolved[]— tags outside the contract (a casing typo like{{firstName}}, a field the platform does not carry like{{loyalty_tier}}). Non-empty means the message ships a hole to everyone.personalization.coverage[]— for per-recipient tags, how many sampled recipients actually carry a value.{{first_name}}with 8 empty contact records renders blank for those 8.
warnings[] and holds ready_to_launch at false until it is cleared. The full contract, warning shapes, and fixes are in Validate campaign personalization merge-tags before launch. Gate your pipeline on it: any warning beginning with Personalization blocks launch.
4. Locales: translate the finished template
Translate only after the body is final — a re-translated message re-needs every gate downstream.POST /api/v1/campaigns/templates/translate rewrites a template body for a target language while preserving two invariants:
- Tokens survive verbatim — every
{{token}}stays in place, unchanged. - The opt-out instruction stays exactly once — a carrier requirement the translator enforces.
5. List hygiene: project what the gates will drop
Before you split variants or score deliverability, the dry-run’s list-hygiene projection tells you how much of the deliverable cohort the send-time gates will actually drop — suppression, opt-outs, unreachable addresses, and the Do-Not-Call sample over phone-shaped channels.list_hygiene.dnc.blocked_estimate and formatted_savings_usd alongside audience.deliverable: the first says “roughly N recipients vanish at the send gate,” the second “$X of spend avoided.” When dnc.checked is false — email-only channel, or a sampling failure — treat DNC exposure as unknown, never as clean. A high sampled-hit ratio is the signal to run the batch pre-flight scrub for per-number verdicts and clean the list before you buy variant data on recipients who will never receive anything.
These are tenant-owned controls: the projection reads your own suppression, consent, and registry-sync data and writes nothing. It never messages a carrier.
6. A/B split with a holdout for uplift
With the creative settled, run the A/B test against it. Two disciplines separate a real answer from noise: Split variants, not audiences. Two to four variants, each with a distinct angle — a different hook, value framing, or call to action — assigned evenly or with a weightedsample_percent split that sums to 100. Each recipient sees exactly one variant. AI-generated variants rewrite the base message and keep the same tokens and single opt-out line, so suggestions stay launch-ready.
ab_holdout_percent (1–50) carves a slice that receives nothing during exploration. Without it, you learn which message beat the others; with it, GET /campaigns/:id/holdout-lift tells you whether the winner beat doing nothing — the holdout uplift guide covers both the campaign-wide and the smart-send timing holdouts. Assignment is deterministic per contact, so a paused-and-resumed campaign keeps the same control set, and no experiment surface biases another.
Pick ab_winner_metric to match the campaign’s goal. A click-through promo judged on delivered locks in the variant that arrived, not the one that converted.
7. Voice variants for fallback
When the campaign’s fallback chain ends on voice — or a voice-first design falls back to SMS — the creative work shifts from text to script. The voice leg is a spoken script with an optional guided flow, and the SMS fallback restates the same purpose in one segment with the opt-out line:- Voice script: who is calling, why, and the one action you want (press 1 to confirm, call back a number). Keep it under ~30 seconds spoken.
- SMS fallback: the same purpose compressed, with
Reply STOP to opt out— it may be the recipient’s only touch with the message.
cascade_policy never carries a voice hop — route voice-first chains through the /notify waterfall instead. The full recipe — payload shape, fallback window, TCPA/DNC gates for the voice leg, and the per-hop receipt — is Voice broadcast with SMS fallback, and the broadcast-only variant is voice broadcasts.
8. Deliverability-lab ladder: score, fix, re-score
The final gate before launch is the deliverability lab. It is distinct from the regulatory policy scanner: the scanner answers “is this send legal,” the lab answers “will carriers let it through.” Both run in the wizard’s Review step; from the API the lab is read-only and re-runnable on every edit:- Pre-send score.
content_risk.score(0–100) blends spam-trigger vocabulary with URL/link heuristics — shared shorteners, bare IP links,http://links, link-stuffing. The verdict lands aspass/warn/blockwith per-finding remediation, andpredicted_deliverydiscounts your own per-carrier history by that score. - Retry.
blocking_warningsnon-empty means the body would be filtered or the predicted rate sits at or below 50%. Fix where the penalties came from — branded link domain instead of a shared shortener,https://, pull the urgency vocabulary. - Adjust and re-score. Re-run the lab on the edited body. Anchor on
ready_to_send: trueand an emptyblocking_warnings— gate on the verdict, not a numeric threshold, since scores shift as the keyword table evolves.
9. Template analytics: read what the creative did
After launch, a reused template accumulates its history across every campaign that sent it. Template analytics rolls that into one view —GET /api/v1/templates/{id}/analytics returns cross-campaign totals, delivery/open/click rates, and a per-campaign funnel — so the creative question “which message actually converts” gets answered across campaigns instead of one send at a time. A delivery drop after a template edit is the signal to re-run sections 3–8 on the new copy.
10. Trouble matrix
Related guides
- Send a campaign end-to-end — the flight this bake feeds.
- Build a campaign with the create wizard — the dashboard path over the same endpoints.
- Template-variable catalogue, personalization preview, A/B testing, holdout uplift, list-hygiene projection, deliverability lab, template analytics — the per-step deep dives this walkthrough stitches together.