> ## 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.

# Run an RCS marketing campaign end-to-end

> Take an RCS campaign from reach scan to launch to callbacks: size the audience against your launched agent, bind an approved template, ladder SMS as fallback, pre-flight, launch, and read sent/delivered/read events per template.

# Run an RCS marketing campaign end-to-end

This guide walks the full lifecycle of an RCS campaign: size how much of the audience your agent can actually reach, draft the campaign with the approved template bound to the agent, dry-run the RCS-specific gates, launch, and subscribe to the webhooks that report per-recipient outcomes. It complements the [campaign end-to-end guide](/guides/campaign-end-to-end), which covers the channel-agnostic sequence — everything here is the RCS delta: agent binding, template approval, the capability-based reach scan, and the SMS fallback ladder that decides who still gets the message.

## 1. Prerequisites

* **An agent (bot) launched on at least one carrier.** An RCS campaign sends only through a carrier-launched agent — a `draft`, `pending_verification`, or `pending_launch` bot fails the pre-flight. If you have not onboarded yet, start with the [RCS onboarding guide](/guides/rcs-onboarding); the brand → agent → verify → launch sequence takes days, so do it before any campaign planning.
* **An approved template registered to that agent.** Campaign sends reference templates by name, and only `Approved` templates pass the launch gate. Author and submit it in the [Rich Card Studio](/guides/rcs-rich-card-builder), and read its approval badge on the [Templates tab](/guides/rcs-templates-tab). A template pending carrier review blocks the launch.
* **An audience sized by reach scan.** RCS reachability is per device — a list that looks large can be half non-capable. Run the reach scan in step 2 before you commit the segment to a rich-media send.
* **An API key with the `campaigns:write` scope.** Reads (reach scan, audience preview, dry-run, stats) accept `campaigns:read`; launch requires write.
* **An opted-in recipient audience.** Opt-outs and suppression are enforced at send time; the SMS fallback hop passes through the same opt-out screening as a pure SMS campaign.

## 2. Pick reach: scan before you build

The reach scan samples your segment against your bot and returns the capable share plus a recommended channel mix — the answer to "RCS-only, mixed, or SMS-first" before you draft:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/rcs/reach-scan" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": "bot_4kqzx1",
    "segment_id": "seg_summerVIPs",
    "sample_size": 100
  }'
```

Response — 200:

```json theme={null}
{
  "data": {
    "segment_id": "seg_summerVIPs",
    "bot_id": "bot_4kqzx1",
    "sampled": 100,
    "capable": 63,
    "reach_percent": 63,
    "recommended_channel": "mixed"
  }
}
```

Read the verdict:

* **`rcs`** — most of the segment is capable; a single-channel RCS campaign is viable, but a short SMS ladder still protects the minority.
* **`mixed`** — some recipients are capable, some are not; use the RCS-first ladder with SMS as the terminal hop (step 3). Never ship a `mixed` verdict as RCS-only — every non-capable recipient hard-fails.
* **`sms`** — the segment is mostly non-capable; either keep this send on SMS, or ship the ladder anyway knowing most recipients settle on the SMS hop.

Size expectations, not certification: the scan samples up to `sample_size` contacts (default 50), so treat the percentage as an estimate. The reach-scan recipe in the [SMS-to-RCS upgrade guide](/guides/sms-to-rcs-upgrade) covers per-recipient capability caching when you need finer granularity.

## 3. Build the campaign: template attached, agent bound

Create the draft with `channel: "rcs"` and the approved template name from your agent's roster:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Summer sale — RCS VIP drop",
    "type": "blast",
    "channel": "rcs",
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "message_template": "summer_drop_card_v2",
    "send_time_optimization": "fixed"
  }'
```

The pre-flight resolves `message_template` against the agent's template roster and requires `status: Approved`. A `PUT` to a template resets approval to pending — check the row on the **Messages → RCS → Templates** tab before launch, and sequence template edits between launches, not during them.

**SMS fallback ladder.** To send RCS first and fall back to SMS on terminal failure, pass a `channels` array whose first entry is RCS carrying `rcs_template`, with SMS as the terminal hop:

```json theme={null}
"channel": "rcs",
"channels": [
  {
    "channel": "rcs",
    "order": 0,
    "fallback_on": "failed",
    "fallback_after_seconds": 120,
    "rcs_template": { "name": "summer_drop_card_v2" }
  },
  { "channel": "sms", "order": 1 }
],
"name": "Summer sale — RCS-first with SMS fallback",
"type": "blast",
"audience_type": "list",
"audience_id": "list_summerVIPs",
"message_template": "Summer sale VIP drop — link below. Reply STOP to opt out."
```

When a ladder exists, the top-level `channel` must equal the first entry, and `rcs_template` may only appear on the RCS entry. The `fallback_on: "failed"` trigger moves a recipient to SMS only on a terminal RCS failure (`undelivered` / `rejected`, including the capability short-circuit `RCS_NOT_SUPPORTED`) — it protects reach without double-paying delivered recipients. The SMS hop carries the plain-text body via top-level `message_template`. The [fallback chains guide](/guides/fallback-chains) maps the broader trigger vocabulary.

## 4. Pre-flight: dry-run plus audience preview

Preview the audience with the channel set, so the net projection subtracts suppression rows, RCS opt-outs, and contacts with no reachable address:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/audience/preview" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "audience_type": "list", "audience_id": "list_summerVIPs", "channel": "rcs" }'
```

Two RCS-specific reasons a preview shrinks beyond the standard buckets:

* **No capability.** Contacts whose devices were probed and reported non-capable (or whose handset carrier is not among the agent's launched carriers) drop out of an RCS-only preview. If the unreachable share is large, add the SMS ladder from step 3 or restrict the audience to capability-verified recipients.
* **Template pending.** On a ladder, the RCS hop is treated as unconnected when its template is not `Approved`, and recipients who would only reach the rich message fall to the SMS hop. On an RCS-only campaign, a pending template blocks the launch outright.

Then dry-run the draft:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/dry-run" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

Beyond the standard buckets (net audience, projected cost, quiet-hours estimate, warnings, `ready_to_launch`), an RCS campaign is gated on RCS-specific state:

* **Agent readiness.** The bot must be `launched` on at least one carrier — the pre-flight reads `GET /api/v1/rcs/bots/:id/quality` for the `carrier_statuses` map. A campaign pointing at a draft or pending-launch bot reads unconnected and suppresses `ready_to_launch`.
* **Template approval.** Launch re-resolves `message_template` or the hop's `rcs_template` against the agent's roster; anything not `Approved` means back to the Templates tab.
* **Fallback hop readiness.** On a ladder, the SMS hop requires a dedicated sending number — the pre-flight probes every step, so an unprovisioned SMS sender also suppresses `ready_to_launch`. Details: [pre-launch readiness for fallback chains](/guides/campaign-fallback-readiness).

## 5. Launch and read

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/send" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

A 422 here pointing at a template name goes back to step 3 — re-check approval status. A 402 or a pending-approval shape is the standard balance/approvals gate, not RCS-specific.

Subscribe to both campaign lifecycle events and per-message events; the [webhook events catalog](/webhooks/events) lists every subscribable type, and the [per-channel DLR wiring guide](/guides/wire-dlr-webhooks-per-channel) maps each channel's event names on one receiver.

* **Campaign lifecycle** — `campaign.started`, `campaign.completed`, `campaign.paused`.
* **Per-message outcomes** — `message.sent` (accepted by the carrier hub), `message.delivered` (device receipt), `message.read` (the recipient opened it — RCS reports a read receipt SMS never produces), and `message.failed` carrying the provider error, e.g. capability skip (`RCS_NOT_SUPPORTED`) or a carrier rejection on template content.
* **Replies** — inbound replies arrive as `message.received`; route them into the inbox or your own automation via the [normalized inbound envelope](/webhooks/normalized-inbound-envelope).

`GET /api/v1/campaigns/:id/stats` aggregates the same signals — sent, delivered, read (`opened`), replied — so the dashboard and your webhook-driven pipeline reconcile. On a ladder, each hop's outcome lands on the campaign recipient record; the [cascade tracking guide](/guides/track-a-cascade) is the per-hop receipt surface.

## 6. Per-template analytics

Read per-template rates to decide what to keep sending:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/rcs/analytics/templates?days=30" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

The response compares sent / delivered / read / clicked per template name, so you can find which card earns taps before scaling the next campaign onto it; `GET /api/v1/rcs/analytics/daily?days=30` breaks volume per day to catch a delivery dip after a bot or template change. In the dashboard the **Messages → RCS → Analytics** tab renders the same aggregation, and the [Templates tab](/guides/rcs-templates-tab) shows the counters inline with each template's approval badge.

## 7. Operate after launch

* **Template edits mid-flight.** Every `PUT` to a template resets approval to pending — a live campaign referencing it falls back to SMS (on a ladder) or starts failing (RCS-only). Freeze templates before launch; land edits between launches. The warning lives on the [Templates tab](/guides/rcs-templates-tab) too — sequence edits around launch windows.
* **Rollback to SMS-first.** If RCS delivery dips against a launch's baseline, pause the campaign (`POST /api/v1/campaigns/:id/pause`), edit the ladder so `channel: "sms"` leads, and re-launch; or flip the org-level cross-channel fallback off for blast traffic until the carrier hub recovers. Either way the SMS hop keeps the audience reachable.
* **Re-approve before renewal.** Template approval does not roll into new definitions: when you revise copy for a seasonal reuse, resubmit and wait for `Approved` before scheduling the next send. The approval-state table on the [Templates tab](/guides/rcs-templates-tab) is the checklist.

## Troubleshooting

* **Launch 422: template not approved or not found.** Re-sync the Templates tab and confirm the name — template names are case-sensitive. Only `Approved` templates send.
* **Pre-flight: RCS hop unconnected.** The bot is not launched on any carrier, or the ladder's RCS template is pending. Poll `GET /api/v1/rcs/bots/:id/quality` for the per-carrier `carrier_statuses`.
* **High `message.failed` with `RCS_NOT_SUPPORTED`.** The recipients were never RCS-capable to this agent — shorten your capability cache or restrict the audience to probed-capable recipients; on a ladder, those recipients advance to SMS.
* **Delivered stays high, read trends toward zero.** Rich cards earn taps only if the card itself prompts one — compare the template's rates against the rest of the roster on the Templates tab or the per-template analytics endpoint and iterate on the card content before scaling further.
* **Sends skip recipients on some carriers.** The agent is launched only on a subset of carriers — the org-level `RCS → SMS` fallback or your campaign ladder's SMS hop covers the rest; compare `carrier_statuses` against where your audience sits before scaling traffic.
