> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Campaign lifecycle: draft to completed, blast vs drip vs journey

> The campaign object model and its state machine — the statuses a campaign moves through and who advances each transition, how blast, drip, and journey campaigns differ at runtime, what pauses a campaign, and how terminal outcomes roll into stats, holdout lift, and ROAS.

# Campaign lifecycle

A campaign in Orbit is one object with a real state machine: it is drafted, gated, scheduled, executed — possibly paused along the way — and lands in a terminal outcome that feeds your analytics. Guides walk you through [sending a campaign end-to-end](/guides/campaign-end-to-end) and [A/B variants](/guides/campaign-ab-testing); this page names the model those guides operate on. Read it before you branch an integration on `campaign.status` or design an automation around campaign events.

## The campaign object

Every campaign is one row identified by a `cmp_`-prefixed id, created via `POST /campaigns` and read back via `GET /campaigns/:id`. Behind that one id sit four choices that define the campaign:

* **One channel set** — the primary `channel` plus an optional channel chain (`channels[]`) executed as fallback attempts. In a drip or journey, each step picks its own channel.
* **One audience** — a list, a segment, a filter, or `audience_type: "all"` resolved at execution time. Audiences are re-resolved when a step executes, not frozen at draft time.
* **One template (or several)** — the body rendered for each recipient, with per-recipient variables. A/B variants become multiple template arms under the same campaign.
* **One mode** — `blast`, `drip`, or `journey` (plus `ad` for paid-audience sync campaigns). The mode decides how the audience is treated at runtime; see [Blast vs drip vs journey](#blast-vs-drip-vs-journey).

Everything else — throttle, quiet hours, send-time optimization, credit cap, holdout — is configuration layered on top of those four choices.

## The status machine

A campaign's `status` field is the branch point for every integration. The list endpoint (`GET /campaigns`) validates `?status=` and `?statuses=` filters against this set:

| Filter status      | Runtime source                                                                                       | Live or terminal                                |
| ------------------ | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `draft`            | Your `POST /campaigns` create payload                                                                | Live — editable                                 |
| `pending_approval` | The approval gate, when your trust tier requires supervisor sign-off before launch                   | Live — parked                                   |
| `scheduled`        | You set a future `scheduled_at`; the launch writes `scheduled`                                       | Live — waiting on the scheduler                 |
| `sending`          | The execution service claims the campaign and fans out batches                                       | Live — active                                   |
| `running`          | The execution service, between batches (drip steps pending, journey waits)                           | Live — active                                   |
| `aborting`         | You request a cancel on an active campaign; observed while in-flight batches drain                   | Live — an in-flight detail, not a final outcome |
| `completed`        | The execution service, when the audience is exhausted with no failures that leave work unfinished    | Terminal                                        |
| `partially_failed` | The execution service, when the campaign finishes but a slice failed terminally                      | Terminal                                        |
| `paused`           | You, quiet hours, or the credit-cap governor (see [What pauses a campaign](#what-pauses-a-campaign)) | Live — resumable                                |
| `cancelled`        | You confirmed the cancel; `aborting` resolves here                                                   | Terminal                                        |
| `failed`           | The execution service hit a blocking error before completing                                         | Terminal                                        |

The happy path is `draft → scheduled → sending → running → completed`. `sending` and `running` are both live: `sending` marks active fan-out, `running` is the steady state of a campaign between batches (a drip holding for the next step delay, a journey waiting on events). The dashboard's "Active Now" rollup counts `running`, `aborting`, and `sending` together, so filter with `?statuses=running,aborting,sending` — a bare `?status=running` misses the others and disagrees with the tile.

Each transition has a distinct owner:

| Transition                                            | Who advances it                                                                                                                                                               |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| → `draft`                                             | You, at `POST /campaigns`. The campaign does nothing until you launch it.                                                                                                     |
| `draft → pending_approval`                            | The trust-tier governor, when your launch requires supervisor approval. The launch returns `202` with an approval id; a supervisor then approves or rejects in the dashboard. |
| `pending_approval → draft`                            | The approval gate, on rejection — the campaign goes back to draft for edits, or to `scheduled`/`running` on approval.                                                         |
| `draft → scheduled`                                   | You, by launching with a future `scheduled_at` (`POST /campaigns/:id/send`). An immediate launch skips `scheduled` entirely.                                                  |
| `scheduled → sending`                                 | The scheduler, at fire time. It claims eligible `scheduled` campaigns and hands them to the execution service.                                                                |
| `sending → running`                                   | The execution service, once the initial fan-out settles into steady state.                                                                                                    |
| `running → completed` / `partially_failed` / `failed` | The execution service, when the audience is exhausted or a blocking error lands.                                                                                              |
| `running → aborting → cancelled`                      | You, via the cancel endpoint. `aborting` is observed while in-flight batches drain; nothing else writes it.                                                                   |
| `running → paused`                                    | You (manual pause), the compliance layer (quiet-hours deferral), or the spend governor (`credit_cap_reached`).                                                                |
| `paused → running`                                    | You resume it, or the deferring condition clears (quiet hours end) and the dispatch loop picks it back up.                                                                    |

Sequence for a scheduled blast that completes cleanly:

```
You           Scheduler          Execution service       Provider/carrier
 | POST /campaigns (draft)
 |----draft---->|                |                       |
 | launch with scheduled_at
 |----scheduled->|               |                       |
 |               | fire time     |                       |
 |               |---claim------>|                       |
 |               |               | sending: fan out      |
 |               |               |------ submit -------->|
 |               |               |<----- accept ---------|
 |               |               | running: drain batches|
 |               |               |------ DLRs arrive ---->|
 |               |               | completed             |
 |<--GET /campaigns/:id returns completed, stats attached--|
```

## Blast vs drip vs journey

The `mode` you set at create time picks one of three runtime models:

**Blast** — one-shot fan-out. The audience resolves once, the template renders per recipient, and the execution service pushes batches at the campaign's throttle until the audience is exhausted. One status arc, one send per recipient, done. A blast with `send_time_optimization: "recipient-optimal"` is still a blast — each recipient gets their own `scheduled_at`, but still a single send.

**Drip** — an ordered step sequence (`steps[]`, up to 50). Each step has its own channel, template, delay, and optional send condition. A contact advances through the steps in order; each step evaluates its `condition` against the **previous step's outcome** for that contact — e.g. send step 2 only to contacts whose step-1 message is `not_delivered`. Because conditions gate on delivery statuses, they are channel-dependent:

* **Read gates (`read` / `not_read`) are valid only after a read-capable channel** — WhatsApp, RCS, email, Viber, or Telegram. A read gate behind an SMS, MMS, voice, fax, or push step can never match (those channels top out at `sent`/`delivered`/`failed` and never report `read`), so the API rejects the combination with a 422 instead of silently dropping every contact at that step.
* **`delivered` / `not_delivered` conditions work after any channel.**

Drips also support exit goals (`exit_goals`): when a contact hits a goal event (a purchase, a reply, a conversion), they leave the sequence early — converted contacts stop receiving reminder steps.

**Journey** — event-driven. Instead of a fixed step list, a journey is a graph whose routers move each contact based on what they do: entry routers decide who enters (enrollment events, segment membership, single-contact enrollment), condition routers branch on contact behavior, and exit routers remove contacts on goal or terminal events. A journey sits in `running` for as long as entries keep arriving — unlike a blast or drip, the audience is a stream, not a fixed set. Journeys back the contact-360 "enroll" action and the single-contact enrollment endpoint, so one individual contact can enter a journey mid-flight.

Mode is not an afterthought — pick it against the shape of your audience (fixed set vs. arriving stream) and whether your follow-up logic is time-based (drip) or behavior-based (journey).

## What pauses a campaign

Three forces can move a live campaign to `paused`, and each resumes differently:

1. **Spend governor — `credit_cap_reached`.** If you set `credit_cap_usd_cents` at create or update time, the execution service refuses any batch that would push `credit_spent_usd_cents` past the cap and auto-pauses the campaign, writing `metadata.auto_paused_reason: "credit_cap_reached"`. Raising the cap (or clearing it with `null`) and resuming puts the campaign back to `running`. Read `auto_paused_reason` before treating a paused campaign as an operator action — it tells you the governor fired, not a human.
2. **Quiet-hours deferral.** When `quiet_hours_enabled` is set on the campaign, the dispatch loops defer sends inside the recipient-local quiet window (cross-midnight windows like 21:00 → 07:00 are supported). The campaign stays live and resumes automatically when the window closes — you do not resume anything.
3. **Manual pause.** You pause it from the dashboard or API. Only you resume it.

Distinguish `paused` (live, resumable) from `aborting`/`cancelled` (the exit path). A cancelled campaign cannot be resumed; a paused one can.

## Send-time optimization

`send_time_optimization` picks how each recipient's send time is computed:

* **`fixed`** (default) — every recipient is sent at the campaign's single batch time. One `scheduled_at` for the whole audience.
* **`recipient-optimal`** — the execution worker computes a per-contact `scheduled_at` from that contact's engagement profile (the smoothed best hour-of-day / day-of-week signal the platform has for them, per channel) and spreads the audience across the next 24 hours. Recipients with insufficient engagement signal fall back to 10:00 local time.

The legacy boolean `smart_send` maps onto the same behavior (`true` ≡ `recipient-optimal`); if you send both, the explicit enum wins. Whether you send `fixed` or `recipient-optimal`, quiet hours still apply — an optimal time inside a recipient's quiet window defers.

Add `smart_send_holdout_pct` (0–50) and a slice of the audience is held out at the fixed batch time as a control group. After the campaign completes, `GET /campaigns/:id/smart-send-holdout-lift` measures the incremental lift of optimal timing against that holdout.

## Terminal outcomes and analytics

When a campaign leaves the live set, its outcome lands in one of four terminal statuses — `completed`, `partially_failed`, `cancelled`, `failed` — and the analytics layer treats them differently:

* **`completed` / `partially_failed`** roll into campaign stats: delivered/read rates, per-step drip breakdowns, and per-variant A/B results. Revenue attribution joins those outcomes against conversion events to produce spend, attributed-revenue, ROAS, and ROI per campaign. With a holdout configured, lift endpoints compare treated vs. control cohorts.
* **`cancelled`** reports what was sent before the abort; nothing after the cancel counts.
* **`failed`** reports the blocking error; partial sends are visible in stats but the campaign never reached its audience end-to-end.

For the endpoint payloads that carry these numbers, see the [campaigns API reference](/api-reference/endpoints/campaigns). For the message-level states a campaign's sends move through (the `delivered`/`read`/`submitted_no_receipt` vocabulary drip conditions gate on), see the [delivery lifecycle](/concepts/delivery-lifecycle). To put the model to use, [send a campaign end-to-end](/guides/campaign-end-to-end).
