Skip to main content

Build a blast or drip campaign with the create wizard

The create wizard is the dashboard path for turning on a blast or drip campaign without writing a request. It runs five ordered steps — Setup → Audience → Message → Schedule → Review — and renders the readiness, localization, and compliance panels inline before launch. This guide walks each step in order, maps every form field onto the POST /campaigns body so you can switch to the API mid-flow, covers drafts, the launch validation gates, and what the campaign detail page gives you after launch. Two worked examples land at the end: an SMS promo blast, and a three-step onboarding drip whose second step goes out on WhatsApp.

1. Wizard or API — pick the right entry point

Use the wizard when a person is driving the campaign: it enforces validation on every step transition, runs the pre-flight dry-run visually, and gates Launch on the same checks the API enforces server-side. Use the API (see Send a campaign end-to-end) when you’re provisioning campaigns from your own system, templating them, or running them from CI. Launch requires an owner, admin, or developer seat — the same role gate POST /campaigns enforces. A billing or supervisor seat can compose a campaign but the Review step’s launch call is rejected with 403 INSUFFICIENT_PERMISSIONS; the wizard keeps your input locally so an owner can finish the send. Everything else in this guide — drafts, previews, dry-runs — works in either surface, because the wizard is a client of the same public API. The wizard lives at Outbound → Campaigns → Create campaign in the dashboard navigation.

2. Step 1 — Setup: name, type, and channel

Setup picks the campaign type (blast, drip, or journey), a name, an optional description, and the primary channel. It also validates the omnichannel fallback chain when you stack channels.
  • Name (1–200 chars), mapped to name. Description (≤1000 chars) → description.
  • Typetype. Blast and drip stay in this wizard; journey and ad campaigns route to their own surfaces (the journey canvas, or the ads console).
  • Channelchannel. The selector lists every live or beta channel from the channel registry; “coming soon” channels render disabled and can’t be submitted.
  • Fallback chain (optional) → channels. An ordered list of up to 5 channels. Each non-final entry carries fallback_on (failed / no_delivery / no_engagement) and fallback_after_seconds (60–604,800 seconds). The last entry is terminal. The wizard enforces the same invariants the API does: monotonic order, no duplicate channels, no fallback fields on the final entry. A single-channel campaign sends only the top-level channel.
  • Sender selection (SMS/MMS only) — an optional “Send from” picker for your dedicated numbers, and an optional Messaging Service selector that maps a campaign to a sender pool, opt-out list, and throughput cap. Both persist to variables.from and variables.messaging_service_id; leave either empty to keep account-wide auto-select. Outbound SMS/MMS always terminates through the Devotel wholesale network either way — this chooses which of your identities it presents.

The RCS sender panel

Picking rcs mounts a per-channel sender panel under the capabilities card. It derives its state from your RCS sender roster with three outcomes: verified (the campaign originates as a carrier-verified agent, with branding: “Sends from your verified sender”), pending (agents mid-verification — build now, launch when cleared), or none (no sender set up; a deep-link to Settings → Channels → RCS). Whichever state, the panel also notes the automatic SMS fallback: recipients whose handset or carrier can’t receive RCS are delivered by SMS on the same dispatch, and you can additionally add SMS as an explicit chain fallback for the no-delivery or no-engagement cases.

3. Step 2 — Audience: lists, segments, CSV, manual picks, CRM

Audience resolves who the send targets. Pick a strategy card: The Choose contacts card mounts a searchable picker over your contacts: debounced search, a recent-contacts fallback when the query is empty, chip/remove handling, and a 10,000-contact ceiling matching the server-side audience limit. The picker submits raw contact ids; the send path re-resolves each one to a full audience entry at launch. Salesforce list-view sources additionally ask for a target object (Contact or Lead). Below the strategy, an estimated recipient count updates — the wizard reuses the audience preview (POST /campaigns/audience/preview) net-projection so the count you see already discounts suppression rows, channel opt-outs, and unreachable addresses. The count snapshot is saved with the campaign (variables.estimated_recipients) so a scheduled send shares it with the Review step.

4. Step 3 — Message: content, templates, A/B variants

The composer holds the campaign body (message_template, up to 100,000 chars — sized for HTML email), personalization tokens in {{token}} form, and per-channel character counters for SMS and other short-body channels.
  • Template picker. Load any approved template from your template library (the /outbound/templates collection) by name. Loading a template writes its body into the composer — so anything you start from stays editable before launch. Channels with pre-registered content (WhatsApp) resolve against approved templates rather than freeform copy.
  • Email subject — email campaigns get a subject field (subject, ≤500 chars), interpolated with the same tokens at send time. Drips carry a per-step subject instead, so this field only applies to blast email.
  • A/B testing. Enable to add a Variant B (and optionally C/D) with a traffic split. Two variants allow any integer split summing to 100 (presets 50/50, 70/30, 80/20 are shortcuts); three-plus variants get fine-grained per-variant allocations. You also pick the winner metric (delivered / opened / clicked / replied) and an optional holdout percent (≤50) for lift measurement. Everything here persists through a variables.ab_test structure plus variant rows synced at launch.
  • Personalization preview. A recipient picker renders the body with a sample contact’s fields substituted — custom fields (e.g. {{customer_tier}}) included — and the phone mock shows the resolved sender identity (a dedicated number or the shared Devotel sender for SMS; your brand name for brandable channels).

Localization: the translate-locales dialog

The “Translate for locales” button calls the template translator (POST /campaigns/templates/translate) which rewrites the current body into one or more locales while preserving tokens and the opt-out instruction. You review each generated variant in a dialog — only variants you explicitly approve are saved onto the campaign as variables.locale_variant_set, and the send pipeline routes each recipient to the variant matching their contact’s language, falling back to the base body when no variant matches. When to translate vs. fork: use the locale variant set when one campaign with per-recipient language routing is the right unit of measurement. Fork into one campaign per locale instead when different locales need different send times, different sender registrations, or separate holdout/ROI accounting.

5. Step 4 — Schedule: fixed time, recurrence, throttling, windows

Schedule decides when and how fast the send fires.
  • Send now or later. A one-off send (recurrence: once → no variables.recurrence) can fire immediately or at a fixed scheduled_at timestamp interpreted in the timezone you pick. The wizard rejects past instants and converts the wall-clock to a UTC ISO string via the same timezone you selected.
  • Recurrence. For scheduled sends, optionally pick a cadence — daily, weekly (with weekday selection), monthly (with day-of-month), or custom cron (5 fields). Every recurring campaign requires an explicit end condition: never (requires the operator to tick the confirm box acknowledging indefinite sending — a hard gate, not a passive banner), after N occurrences, or on an end date. The recurrence block persists to variables.recurrence only when the send is scheduled and recurring, so a switched-back “Send now” send never inherits a stale cadence.
  • Throttle. A messages/second ceiling (throttle_rate, 0–10,000). Zero means “no explicit ceiling” — the platform default of 50 msg/s applies.
  • Business hours. Restricts sends to business-hours windows (variables.business_hours_only).
  • Number validation (HLR). An optional per-recipient lookup (variables.validate_numbers) checked before send, priced separately from the message rate; the wizard projects its cost including recurring-occurrence totals.
  • TCPA window notice. For voice and SMS, sends queue only in the 8 AM–9 PM recipient-local federal window; the step renders a banner explaining that out-of-window recipients are deferred rather than failed. The window is enforced server-side at send time; see the send-gating model.

6. Step 5 — Review: readiness, localization recap, compliance, launch

Review assembles the launch surface: the cost estimate, the sending-readiness panel, per-locale variant recap, the compliance checklist, and the launch/submit-for-approval action.

Sending-readiness panel

Four enumerated rows fold the live preflight into a verdict (pass / attention / unverified — never a silent pass):
  • Channel — a channel is selected.
  • Route — outbound SMS/MMS always routes via the Devotel wholesale network; other channels report “unknown” rather than a fabricated pass (their setup happens under Settings → Channels).
  • Channel connection — whether this account has a dedicated sending number for the channel. SMS-family is the only channel the preflight probes, so WhatsApp/RCS/voice/email connection reads “unknown” rather than green.
  • Sender identity — the exact from-identity the send will originate from (your dedicated number’s number, the shared Devotel sender when no dedicated number is attached, or an email sender name).
A not-connected dedicated sender is attention, not blocked: outbound still ships via the shared Devotel sender, and the checklist adds a required acknowledgment before Launch enables.

Compliance checklist

The checklist mixes machine-verified rows (locked to a real signal, not toggleable) with self-attestation rows:
  • Sending setup (machine) — satisfied when the live preflight returns no blocking issue. Rendered in an advisory register when only advisory issues exist (no dedicated number attached, or a regulated-country destination without an approved profile), and unsatisfied while the check is errored or loading. Remediation deep-links (buy a number, set up a compliance profile) render beside it.
  • Opt-out language (machine) — for freeform channels (SMS, MMS, RCS), every launch body — the primary variant plus each non-empty A/B variant — must contain carrier-accepted opt-out language (STOP / UNSUBSCRIBE / OPT OUT / cancel). WhatsApp template sends and email are exempt (Meta handles template opt-out; ESPs carry unsubscribe in headers). For multi-variant tests, an unsatisfied hint names the specific missing variant.
  • Recipients opted in (attestation) — confirm the audience consented.
  • Content reviewed (attestation) — confirm the copy is accurate.
  • Shared-sender acknowledgement (attestation, conditional) — appears only when no dedicated SMS number is connected; the send ships on the shared Devotel sender either way, but launch requires the conscious confirmation.
  • Channel-readiness acknowledgement (attestation, conditional) — appears only for channels the preflight can’t verify (WhatsApp, RCS, voice). Unlike SMS, these have no shared-sender fallback, so their connection needs an explicit confirmation before launch.
Launch is gated on every machine row satisfied plus every attestation row ticked — see the gate list below.

Launch vs. submit for approval

When the org level requires approval (campaigns/approvals), the primary action becomes Submit for review: the launch parks in the approvals queue instead of sending, and an approver (owner/admin with approval rights) releases it from Outbound → Approvals. The wizard refuses to render a confident one-click launch while the approval policy is unknown.

7. What the wizard posts — field-to-body mapping

Every Review-step launch call chains the same public endpoints the API guide uses, in this order:
  1. POST /campaigns (or PUT /campaigns/:id for an edit) — the campaign row.
  2. POST /campaigns/:id/steps — drip steps (drips only).
  3. POST /campaigns/:id/variants — A/B variant rows (3+ variants).
  4. POST /campaigns/:id/send — the launch. For a future scheduled_at, this moves the campaign to scheduled status without dispatching; the scheduler fires it at the configured time. No call → stuck in draft.
The body mapping from wizard field to API field: If a mid-chain call fails (steps/variants/send), the campaign row already exists, and the Review step reports the partial-launch state instead of silently leaving a created-but-idle draft — re-launching updates the existing row rather than minting a duplicate.

8. Drafts: saving and resuming

Save draft (Review step) persists the campaign as a draft row. A draft reopens through Campaigns → Drafts tab in the list, or directly via the ?edit=<id> deep-link the draft card offers. Reopening routes edits to PUT /campaigns/:id, so repeated saves never mint duplicates. When a launch attempt fails on a non-permission error, the wizard still keeps the draft it minted locally, so retrying updates rather than duplicates. A draft saved mid-wizard carries its complete form — schedule, recurrence, A/B, locale variants — so “Save draft” from a scheduled send restores the same schedule on reopen.

9. Launch gates — what disables the button, and the error you get

The Review step disables Launch/Submit and lists the blocking reasons beside the button. Gates, in check order: Server-side, POST /campaigns/:id/send enforces the same checks — the dry-run buckets (Send a campaign end-to-end) map one-to-one to the Review-step panels, so a failed launch from the API can be diagnosed on the wizard’s dry-run report (audience / cost / quiet-hours / warnings).

10. After launch: the campaign detail page

Launching puts the campaign onto the tracking page (Campaigns → click the row). What you get:
  • Stats panel — live totals for sent, delivered, failed, opened, clicked, replied; polled while running.
  • Pause / resume / cancel — pause stops dispatch and preserves state; cancel terminates (in-flight sends complete); resume continues from where it stopped. A campaign that hit its credit_cap_usd_cents auto-pauses and tells you why.
  • Drip analytics (/campaigns/:id/drip-analytics) — per-step sent/delivered/read/failed plus sequence progress, for drips.
  • Holdout-lift console (/campaigns/:id/holdout-lift) — treatment vs. control conversion-rate delta with confidence interval when you set a holdout.
  • ROAS (/campaigns/:id/roas + touchpoints) when you attribute revenue.
  • Lifecycle webhookscampaign.started/paused/resumed/cancelled/completed plus drip-step events; subscribe under webhooks.

11. Troubleshooting

  • Wizard stuck in draft. The campaign exists but no send fired — almost always a missing POST /campaigns/:id/send (the wizard reports the partial-launch chain). Reopen the draft from the Drafts tab and launch again; the wizard will update the existing row.
  • Launch button disabled. The inline reason list names the gate — work down section 9’s table. The most common are an empty net audience (all suppressed/opted-out/unreachable — rerun the preview with a channel) and a missing opt-out line.
  • “403 INSUFFICIENT_PERMISSIONS” on launch. The launch needs owner/admin/developer; a supervisor or billing seat must hand the draft to an owner — the wizard preserves the entry so an owner completes it.
  • Dry-run errors from the API. Match them to the dry-run buckets — audience.deliverable: 0 maps to the zero-recipients gate; cost.sufficient: false to the wallet gate; warnings[] (“no provider registered”, “quiet hours fully closed”) to the advisory panels on the Review step.
  • Recurring send won’t advance past Schedule. Recurrence requires an explicit end condition; a “Never” end requires the confirm tick. Cron needs five fields.

Worked example 1 — SMS promo blast

A scheduled SMS blast to a VIP list with a holdout, no recurrence.
  1. Setup: name Retail promo — summer sale, type blast, channel sms.
  2. Audience: Contact List card, pick list_summerVIPs; the estimate shows net reach.
  3. Message: body Hi {{first_name}}, early access to our summer sale: 25% off through Sunday. Reply STOP to opt out.
  4. Schedule: schedule a fixed future time, recurrence “once” (no end condition demanded), throttle blank (default).
  5. Review: pass compliance (opt-out language is in the body), launch.
The equivalent API path — the exact fields the wizard sent — folds to the end-to-end guide’s worked example at POST /campaigns with type: "blast", channel: "sms", audience_type: "list", variables.campaign_holdout_percent, and POST /:id/send (see Send a campaign end-to-end).

Worked example 2 — three-step onboarding drip

A welcome drip on signup: welcome email first, WhatsApp follow-up second, SMS nudge third.
  1. Setup: type drip, primary channel can be any single one — per-step channels override it.
  2. Audience: segment new-signups.
  3. Message: the drip step editor lists steps you add one at a time. Step 1: email with a subject (Welcome to Acme). Step 2: WhatsApp — if your account has an approved WhatsApp template, it routes as a template send; the guard re-checks against your approved catalog before launch. Step 3: SMS.
  4. Set each step’s delay (minutes/hours/days/weeks — converted to minutes in the payload), and optionally a per-step send condition (e.g. “previous step read”).
  5. Schedule: send now (the kick-off sends immediately; steps follow per their delays).
  6. Review: launch. Drip analytics then report per-step numbers.

Worked example — manual picker, recurring schedule, RCS sender panel

Manual roster: Audience step → Choose contacts → search contacts by name/number → chips accumulate as you pick → the roster becomes variables.manual_contact_ids on the campaign. Reuse for exec outreach or one-off regulatory notices. Recurring schedule: Schedule step → send later → pick weekly recurrence with Mon/Wed/Fri → end “after 20 occurrences”. The payload becomes variables.recurrence: { recurrence_type: "weekly", recurrence_days: ["mon","wed","fri"], recurrence_end_type: "after_count", recurrence_end_count: 20 }. RCS sender panel: Setup step → pick rcs → the panel below the capabilities card names the verified sender (or the “not yet set up” link). It also notes the automatic SMS fallback every RCS blast gets for non-RCS handsets.