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.
  • Type → type. Blast and drip stay in this wizard; journey and ad campaigns route to their own surfaces (the journey canvas, or the ads console).
  • Channel → channel. 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 webhooks — campaign.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.

Worked implementation — the wizard’s flow as API calls

The section-7 table maps every form field onto an API key. This appendix assembles those keys into the actual requests, so the same campaign the wizard drives can be provisioned from your own system. It mirrors a three-step email drip — the worked example 2 shape — and stays compact on purpose: the full lifecycle with audience preview, dry-run report walkthrough, and analytics lives in Send a campaign end-to-end.

Step 1 — Create the drip as a draft

Build the campaign row first. The body below is the wizard’s field table assembled end to end: name, type: "drip", channel: "email", a segment audience, a scheduled kick-off, an exit goal, and a subject the email steps inherit.
A drip puts its welcome message on message_template; the follow-ups go on steps next. scheduled_at is optional — omit it and the kick-off fires when you launch.

Step 2 — Attach the drip steps

POST the follow-up steps the wizard’s drip editor collects, with each delay already converted to minutes (the wizard’s minutes/hours/days/weeks selector does this conversion for you; the API takes minutes only). An optional per-step condition guards a step on the previous one being read.

Step 3 — Launch

Launch with the send call. The kick-off message goes out, and the drip worker advances each recipient through the steps on their configured delays.
Two successful shapes come back, both with the standard data / meta.request_id envelope:
  • 202 Accepted — the normal launch. data.status is sending (immediate kick-off) or scheduled (a future scheduled_at filed the campaign with the scheduler — it fires at that time).
  • 202 Accepted with gated: true — your org requires supervisor approval. The launch parked as pending_approval with an approval_id; an owner or admin releases it from the approvals queue, and nothing dispatches until they do.

Step 4 — The two rejections worth handling

403 INSUFFICIENT_PERMISSIONS — the launch role gate. The API key’s organization membership is seated as billing or supervisor: compose and draft actions pass, but the send call needs an owner, admin, or developer seat. The wizard hits the same gate and preserves your input for an owner to finish; over the API, hand the campaign id to an owner-seated key.
422 COMPLIANCE_NOT_SATISFIED — the compliance checklist the Review step renders is also enforced server-side at launch. A freeform SMS-family step body without carrier-accepted opt-out language (STOP / UNSUBSCRIBE / OPT OUT / cancel) refuses to launch; the error names the failing check. Fix the body and re-send — the campaign row already exists, so the retry updates rather than duplicating.
Other launch rejections follow the same envelope: 402 INSUFFICIENT_CREDITS when the wallet can’t cover the pre-send estimate, 400 AUDIENCE_TOO_LARGE past your tenant’s audience cap, and 422 VALIDATION_ERROR for a malformed body.

Step 5 — The dry-run sequence

Section 1 split the flow as “wizard or API.” Programmatically the same split is: create the row, refine it with updates, dry-run, then send. Nothing dispatches before the final call, so you can iterate freely.
The dry-run response buckets (audience.deliverable, cost.sufficient, quiet_hours, warnings[]) map one-to-one onto the wizard’s Review-step panels — a rejected launch from the API can be diagnosed on the wizard’s dry-run report before you retry.