Run a WhatsApp marketing campaign end-to-end
This guide walks the full lifecycle of a business-initiated WhatsApp campaign: pick an approved template, draft the campaign with WhatsApp-specific fields, dry-run it, launch, and subscribe to the webhooks that report per-recipient outcomes. It complements the campaign end-to-end guide, which covers the channel-agnostic sequence — everything here is the WhatsApp delta: template binding, Meta’s gates, and the read/reply callbacks SMS never produces.1. Prerequisites
- A connected WABA with at least one approved
marketingtemplate. Business-initiated WhatsApp sends leave only as templates — there is no free-form blast equivalent. If you have not onboarded yet, start with the WABA setup guide; the 24-hour window guide explains why templates are the only permissible content outside an open customer-service window. - An API key with the
campaigns:writescope. Reads (audience preview, dry-run, stats) acceptcampaigns:read; launch requires write. - An opted-in recipient audience. Opt-outs and suppression are enforced at send time; Meta additionally throttles marketing-template delivery for low-quality WABAs, so engagement-quality audiences matter more here than on SMS. Recipients outside the 24-hour window are reached by the template; recipients with an open window still count toward your per-recipient messaging-limit tier.
2. Pick and bind the approved template
The campaign references a template by name, and the launch pre-flights it against your locally synced template registry. A template whose Meta status isPENDING, REJECTED, PAUSED, or DISABLED fails the launch with a 422 — check the status under Channels → WhatsApp → Templates before you draft.
For a single-channel WhatsApp campaign (blast or scheduled), put the template name in the top-level message_template field. For an omnichannel fallback chain, bind the template to the WhatsApp chain entry instead — an SMS or email step has no template name, so the schema rejects whatsapp_template anywhere except a channel: "whatsapp" entry:
If your org’s campaign metadata declares a
template_category_intent (e.g. MARKETING), launch also verifies Meta’s category on the template matches the intent — an AUTHENTICATION OTP template cannot ride a marketing blast.
3. Create the draft
draft status; nothing sends until step 6. Per-recipient variable values ({{1}}, {{2}}, …) resolve from contact fields the same way {{token}} personalization does on other channels — preview them with the audience sample in step 4 before launch.
Fallback chains. To try WhatsApp first and fall back to SMS on terminal failure, pass a channels array whose first entry is WhatsApp carrying whatsapp_template, followed by an SMS entry:
channel must equal the chain’s first entry, and whatsapp_template may only appear on the WhatsApp entry.
4. Preview the audience
channel: "whatsapp" so the net projection subtracts suppression rows, WhatsApp opt-outs, and contacts with no WhatsApp-reachable number. A recipient whose only address is an email disappears from a WhatsApp preview — that cohort is your unreachable count.
5. Dry-run: the WhatsApp gates
- Template pre-flight. Launch resolves
message_templateagainst your synced template registry and requiresstatus: "approved". A missing template name means the local registry has never seen it — sync templates from Meta, then retry. - Quality rating. If your WABA’s quality rating has dropped to red, the campaign auto-pauses with reason
whatsapp_quality_redrather than burning quota on sends Meta will throttle anyway. Clear the quality issue in Channels → WhatsApp → Health, then resume the campaign. - Messaging-limit tier. The dry-run projects cost against the same resolver the send path uses, but the audience size must also fit your current per-24h unique-recipient tier (250 on a new WABA, scaling to unlimited). Check your tier under Channels → WhatsApp → Health before launching at a large list; an over-tier audience fails recipients at send time, not at launch.
6. Launch
7. Callbacks: what WhatsApp reports that SMS cannot
Subscribe to both the campaign lifecycle events and the per-message events; the webhook events catalog lists every type your endpoint can subscribe to.- Campaign lifecycle —
campaign.started,campaign.completed,campaign.paused(including thewhatsapp_quality_redauto-pause),campaign.drip_step.sentfor drip shapes. - Per-message outcomes —
message.sent(accepted by the provider),message.delivered(device receipt),message.read(the recipient opened it — a signal SMS never produces), andmessage.failedcarrying the Meta error code, e.g.(#132015) too many business messageswhen Meta throttles marketing deliveries. - Replies — an inbound reply inside (or opening) the 24-hour window arrives as
message.received. Route it into the inbox or your own automation; the normalized inbound envelope describes the payload shape, and the webhook consumer guide covers signature verification, retries, and dead letters.
GET /api/v1/campaigns/:id/stats aggregates the same signals — sent, delivered, read (opened), replied — so the dashboard view and your webhook-driven pipeline reconcile.
8. Read rate discipline
WhatsApp surface a read receipt SMS lacks; use it. If delivered stays high but read and replied trend toward zero, Meta’s per-user marketing-frequency capping is already filtering your sends before recipients see them — shrink the audience toward engaged cohorts or move the same content into the 24-hour window as a free-form follow-up instead of another marketing template. Read the 24-hour window guide for the rules on when a reply opens that window, and the pricing guide for how marketing vs. utility template categories price each conversation.Troubleshooting
- Launch 422: template not found locally. The template registry has not synced the name — pull templates from Meta (Channels → WhatsApp → Templates → Sync) and retry. Re-check spelling; template names are case-sensitive.
- Launch 422: template status is not approved. Resubmit or pick an approved template. Only
APPROVEDtemplates can send;PAUSEDandDISABLEDcount as blocked. - Campaign auto-paused with
whatsapp_quality_red. The WABA quality rating dropped to red. Resolve the quality issue, thenPOST /api/v1/campaigns/:id/resume. - High
message.failedwith Meta code(#132015). Meta is capping how many marketing-template messages one recipient receives in a window. This is recipient-side filtering, not an Orbit failure — narrow the audience or change the message category. - Audience preview resolves to zero. Re-run the preview with
channel: "whatsapp"— the net projection names the emptying cohort (all opted out, no reachable WhatsApp number).