Your first multi-channel campaign: SMS with WhatsApp fallback
For the full campaign lifecycle — approval gates, drip and journey shapes, advanced segments, holdout and ROAS measurement — use Send a campaign end-to-end. This page is the shortest complete path to a single send with a fallback chain.
- Pick the segment
- Preview the message
- Create the campaign with a fallback chain
- Run the preflight gates
- Send
- Watch the delivery log
- Measure one KPI
Prerequisites
- An API key with
campaigns:write(reads below also acceptcampaigns:read). Create one in Settings → API Keys. - An SMS-capable sender you own. Buy one per Your first SMS, end to end step 2. For US traffic, complete 10DLC registration.
- A connected WABA (WhatsApp Business Account) if you use the WhatsApp fallback — Meta bans inbound-only: connect one per WhatsApp onboarding. The WhatsApp hop needs an approved template for business-initiated sends.
- An opted-in segment. Recipients must have given the right consent for the channel you reach them on. Start from a sandbox key (
dv_test_sk_…) and swap a live key in for production.
+18005551234. Outbound SMS/MMS always terminates through the Devotel wholesale network — the sender field chooses only which of your identities it presents.
1. Pick the segment
A campaign on this page targets one segment — a dynamic, filter-based audience — byaudience_type: "segment" with a audience_id. Segments re-evaluate at send time, so an abandoned-cart segment scoped to “cart event in the last 24h” picks up fresh recipients instead of a frozen list.
Count what the segment resolves to before you launch — POST /campaigns/audience/preview returns matching_count plus a 10-contact sample:
channel and the response also returns a net projection: gross matches minus suppression rows, minus channel opt-outs, minus contacts with no deliverable address. If matching_count is 0, fix the segment before going further. The CDP segments guide covers segment definition; the audience pre-flight review guide covers the resolution math.
2. Preview the message
Templates use{{token}} placeholders resolved per-recipient from contact fields. SMS primary carries the campaign-level message_template; the WhatsApp fallback carries its own approved-template identity on the chain entry (the channels array in step 3 — the field binds to the exact chain step because a campaign-level template name is meaningless across heterogeneous channels). Resolve {{first_name}} and per-recipient tokens against sample contacts before launch — campaign personalization preview walks the response shape; a typo’d {{frist_name}} resolves to an empty string at send time instead of failing loudly, so catch it here.
3. Create with a fallback chain
Thechannels array is the fallback chain: an ordered list of up to 8 channels. Each non-final entry carries a fallback_on trigger (failed / no_delivery / no_engagement) and a fallback_after_seconds window (60–604,800 seconds). The last entry is terminal and must not carry fallback fields. The top-level channel must equal channels[0].channel. The full ordering rules live in fallback chains — this step wires the SMS → WhatsApp case:
fallback_on: "failed" advances on a terminal DLR (failed / undelivered / rejected) — the cheapest trigger, moved only when a hard failure is known. fallback_after_seconds is ignored on that trigger but the schema still accepts the value; use it consistently so an operator reading it later can set no_delivery without a shape change. no_delivery advances when no DLR arrives within the window; no_engagement advances on delivered-but-no-open/read — reach for it only after measuring engagement semantics. The WhatsApp hop bills independently per recipient — every chain is per-hop billed, verified in fallback chains §6.
The response returns the campaign id (cmp_…) in data.id — hold it for preflight and launch.
What to skip on your first send
Some surfaces exist for second and third campaigns — defer them deliberately:- A/B variants — A/B testing splits an audience across two message bodies. Measure a single message first.
- Quiet-hour per-campaign overrides — stick to the org-quiet-hours gate (set in quiet hours configuration); per-campaign overrides add a second policy to audit. The preflight dry-run projects the skip estimate under the org gate.
- Holdout control cohorts — a holdout tells you whether the send beat doing nothing. Readership on one KPI first; the holdout uplift guide is there when you need it.
- ROAS attribution — revenue attribution needs conversion-goal wiring and is covered in campaign ROAS attribution. A delivery-rate KPI is a faster first read.
4. Run the preflight gates
The checklist in Outbound compliance pre-flight names the gates a campaign send walks. For this single-send flow run the same pass — wallet, opt-out/suppression, quiet hours, frequency caps, compliance profile — and confirm each item below before launch. Copy the box state from that page; the send path will mechanical-422 anything missed:- Wallet is funded and not paused (
GET /billing/balance→outbound_paused: false). - Suppression list imported and live — Opt-out & suppression lists.
- Quiet hours window chosen — org gate or campaign fallback window, decided deliberately.
- Frequency caps set per channel — Frequency caps.
- Destination compliance profile attached where the market requires it (Compliance).
- DNC check for a sample of recipients — run
POST /compliance/dnc/scrub(up to 500 numbers per request) against the segment; clear rows pass.
channel_waterfall bucket reports the primary channel and the fallback shape — verify it shows sms primary with whatsapp as the declared fallback. A healthy dry-run returns warnings: [] and cost.sufficient: true; anything else names the gate that would block.
5. Send
Launch withPOST /campaigns/:id/send (or set scheduled_at for a future batch). A type: "blast" campaign sends the whole deliverable audience in one pass at the throttle_rate pace (platform default 50 msg/s when the field is 0 or absent). The throttle guide covers pacing; set throttle_rate explicitly once you know your volume.
202s with the campaign back in running state. If your org requires supervisor approval the launch returns a pending-approval state instead — check GET /campaigns/approvals/pending from the approver’s queue.
6. Watch the delivery log
Subscribe tomessage.delivered / message.failed / message.received webhooks on one endpoint before the very first send — the receiver-loop shape is in Your first SMS, end to end step 4, and the per-campaign webhook events are in campaign lifecycle webhooks. For a page-into-the-log read, GET /campaigns/:id/recipients returns rows with per-recipient status and cursors (pagination.has_more / next_cursor):
failed row and the WhatsApp delivered row. Group block rows by gate (opted_out, frequency_capped, quiet_hours_blocked) the way the preflight checklist §8 maps them, and fix the audience or the gate — never resend blindly.
7. Measure one KPI
Pick one KPI per first send and read it back. For this flow the natural one is delivery rate —GET /campaigns/:id/stats totals sent/delivered/failed/opened/clicked/replied:
sent row doesn’t drag the number down. Compare that KPI across the primary and fallback hops on the same recipients, not campaign-wide — a healthy fallback chain shows fail-over adds delivered messages that would otherwise have been lost. When the deliverable number looks right and holds on the second campaign, layer the deferred surfaces (A/B, holdout, ROAS) in.
See also
- Send a campaign end-to-end — the full lifecycle (draft → preflight → launch → measurement).
- Fallback chains — campaign ladders, per-message cascade policies, Verify profile step-up.
- Outbound compliance pre-flight — the gates this page passes.
- Campaign personalization preview — resolve recipient tokens before launch.
- Campaign create wizard — the dashboard path for the same flow.
- CDP segments — create the abandoned-cart segment this guide scopes.