Skip to main content

CSV to launched SMS blast: the operator path end to end

Four deep guides cover the individual pieces of this flow — the CSV import wizard, sender-registration markets, the campaign create wizard, and personalization preflight — but they each stop at their own boundary. This page is the operator path that chains them into one sequence: CSV on disk → launched blast with per-recipient variables, with a verdict you can read recipient by recipient. Walk it once in order and you never have to guess which deep page to consult at which step. Sections 2–5 name the deep page for the step they execute; come back to this page whenever you need “what’s next.”

1. The four files you are operating

The path crosses four surfaces in the dashboard, in this order: Two mend the fall-through you hit when the four guides are read separately:
  • Consent is recorded per contact, not per import. The imported address becomes reachable only when a suppression scan and opt-in state let it through — that is why the template step ends with a checklist, not just a body.
  • The sender is chosen per destination country before the wizard step that launches. Walking back from the launch gate to “pick a sender” is the rework this ordering avoids.

2. Import: customers.csv becomes a saved list

Start under Audience → Import. The wizard accepts a CSV with one row per recipient, carries phone and email as the addressing columns, and maps the rest onto contact fields. The columns this path cares about are the ones your template will dereference — first_name for {{first_name}}, country_code / timezone / language for the locale that quiet-hours and send windows evaluate against, and any business-specific custom field you defined beforehand (see custom fields). Run the import deliberately:
  1. Upload and map. Pick the file, match columns to field keys where the auto-detect guesses wrong, and leave merge_strategy at the default skip unless you are refreshing CRM attributes, in which case choose merge.
  2. Confirm the preview before it writes. The dry-run answers three questions before anything lands — how many rows carry a parseable phone or email, how many match contacts already in Orbit, and how many duplicate inside the file itself. A preview with rows_invalid > 0 or a nonzero in-file duplicate count means fix the export at the source; never commit an import whose preview you cannot explain.
  3. Watch it land. The job runs in the background under Audience → Imports; rows it rejects land in a skipped-rows CSV with a per-row reason column you fix and re-import as a new job. A rollback window of 24 hours covers a mis-mapped run.
  4. Save as a static list. When the job completes, materialize the imported cohort as a contact list (see static contact lists) — the create wizard’s audience step picks lists, not import jobs.
If the import skipped rows for unparseable phone numbers, you have two paths: fix the export (the skipped CSV is keyed by row index for exactly this), or accept that those recipients never make the blast — the audience preview in step 5 will quantify the gap.

3. Sender: the right registered identity per market

Sender selection happens before the create wizard because a destination that requires pre-registration fails at send time, not at import time. Read the per-country registration level from GET /api/v1/compliance/country-rules — none, recommended, or required — and treat anything required as launch-gating until the registration reaches approved. The sender-ID country matrix reads that signal per market; the five hardest (UK, Saudi Arabia, UAE, and the rest it lists) are the ones that surprise operators most. For the blast itself, the choice is one of three:
  • A dedicated number with the destination’s registration approved. This is the only identity a required market accepts.
  • A Messaging Service sender pool with an opt-out list attached — use it when you send on more than one identity and want per-pool throughput and suppression scoping.
  • The shared Devotel sender only accepts markets whose registration level is none or recommended. If your audience spans a required market, the Review step will flag the unregistered destination and launch blocks on it either way.
Whatever you pick, outbound SMS still terminates through the Devotel wholesale network — the sender choice sets which identity is presented, never which route the message takes.

4. Template: preflight before first send

Compose the body once in Outbound → Templates so the blast and any future campaign reuse it (see outbound templates). Use {{token}} merge tags — {{first_name}} is the common case — and run the preflight in this order:
  1. Merge-tag resolution. Every token must match the five standard contact fields, the {{coupon_code}} generator, or a key on the campaign’s variables. A wrong token — {{firstName}} instead of {{first_name}}, an unsupported field like {{loyalty_tier}} that no contact carries — resolves to blank for every recipient, and the send never errors. The dry-run’s personalization block plus its warnings[] array name the unresolved tags; treat any warning that begins with Personalization as launching-blocked until re-run clean. Full contract: validate personalization merge-tags before launch.
  2. Data coverage on the audience. Tags resolve against contact records, so check the dry-run’s coverage hints per standard-field tag — a high miss rate means the CSV carried addresses but not the fields your template needs. Fill the contact data or drop the tag.
  3. Opt-out token present. Every freeform SMS body must contain carrier-accepted opt-out language — STOP, UNSUBSCRIBE, OPT OUT, or cancel. Missing it flips the compliance row red at Review and the launch refuses. On a per-brand custom keyword list, the same must hold — see opt-out lists.
  4. Quiet-hours and list-hygiene dry-run. Run POST /campaigns/:id/dry-run (the create wizard’s Review step runs it inline) and read quiet_hours.skipped_estimate and warnings[] — anything “quiet hours fully closed” or “no provider registered” resolves here, not at send time. The full gate walk-through lives in the outbound pre-flight checklist; the TCPA window and recipient-local handling are in TCPA quiet hours and windows.
The personalizer renders {{token}} against a sample recipient in the personalization-preview panel — use it to eyeball the substitution before the dry-run gates pass.

5. Launch: one audience subset, fallback off, per-recipient readback

With a list, a sender, and a clean template, open the create wizard under Outbound → Campaigns → Create campaign:
  1. Setup. blast, channel sms, pick the sender from step 3 (a dedicated number, a Messaging Service sender pool, or the shared Devotel sender when the destination allows it). Leave the fallback chain empty on the first send — SMS-only keeps the verdict readable.
  2. Audience. Pick the static list from step 2. The wizard mounts the audience preview with the net projection (suppression minus opt-outs minus unreachable) — sanity-check it against your import counters.
  3. Message. Load the approved template. Confirm the per-recipient personalization preview resolves.
  4. Schedule. Send now, or set a future scheduled_at. SMS exits the 8 AM–9 PM recipient-local window as deferred, not failed — out-of-window sends queue and release when the window opens (see send-gating and quiet hours).
  5. Review. The compliance checklist gates launch on machine-verified rows plus attestations; an audience that resolves to zero recipients and a body missing opt-out language are the two blocking failures most often hit.
Launch returns the campaign tracking page. After the send settles, read the result per recipient, not in aggregate — statuses that name why a message reached or missed are:
  • failed — the SMS left Orbit but the carrier rejected it. The status log’s failure reason is the carrier code; permanent failures de-enroll the recipient from further sends on that channel.
  • skipped_suppressed — an opt-out list row or suppression entry caught the recipient at send time. The blast’s own post-launch report and the suppression console between them explain which list caught it.
  • skipped_quiet_hours — recipient-local time fell outside the send window. The recipient still queues and delivers when the window reopens; the skipped count is a projection, not a loss.
  • unreachable — no address on the channel (an imported row with a missing/invalid phone), or the contact had no deliverable SMS identity.
The failure modes most likely to populate failed on a first CSV-import blast are, in order: numbers the import accepted but carriers reject (landline, VoIP-only ranges), destinations the sender registration hasn’t cleared (the step-3 miss), and raw phone formats the import normalized but the carrier route rejects (less common — the import’s own normalization is usually the fix). For the full campaign lifecycle at the API surface — the programmatic equivalent of this walkthrough — send a campaign end-to-end.