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

# Upgrade SMS traffic to RCS per recipient

> Move an SMS audience onto RCS one recipient at a time — probe capability with route-preview, flip the campaign to an RCS-first channels[] ladder with SMS as the terminal hop, validate the whole chain in sandbox, read per-hop DLRs, and gate the rollout on the campaign pre-flight.

# Upgrade SMS traffic to RCS per recipient

RCS is not a fleet-wide switch — reachability is per device. A recipient whose handset and carrier bridge to the RBM hub gets the rich experience; everyone else still needs SMS. This guide chains the upgrade end to end: detect which recipients can receive RCS, re-author the campaign as an RCS-first ladder with SMS as the terminal hop, prove the ladder in sandbox, watch per-hop receipts, and flip to production behind the pre-flight gates.

Two things must already exist before any of this works: an RCS brand and agent that carriers have approved (the [RCS onboarding guide](/guides/rcs-onboarding) walks brand → agent → verify → launch), and at least one SMS sending number for the fallback hop. If your agent is a draft, stop at the org-level fallback in [the RCS channel page](/channels/rcs) and come back after launch.

## 1. Upgrade vs fallback: pick the mechanism

"Upgrade to RCS" means the **primary channel moves from SMS to RCS** and SMS demotes to a fallback hop — not that SMS disappears. Three mechanisms move traffic across channels; pick one per send class:

| Mechanism                                                          | What it does                                                                | Use it for                                                                                           |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Primary-channel upgrade** (this guide)                           | Campaign `channels[]` ladder leads with RCS; SMS is the terminal hop        | Bulk traffic you want to modernize but keep deliverable for every recipient                          |
| **Notify waterfall** — `POST /notify` with an ordered binding list | A single message cascades hop-by-hop with per-hop receipts on one notify id | Transactional one-offs (delivery updates, reminders)                                                 |
| **Campaign `channels[]` ladder without an upgrade**                | Channel listed first stays primary; later entries are fallbacks             | The general chain shape — [the fallback chains guide](/guides/fallback-chains) maps all three planes |

The [fallback chains guide](/guides/fallback-chains) is the full plane map (campaign ladder, per-message `cascade_policy`, Verify step-up). This page is the worked SMS → RCS instance of the campaign ladder.

## 2. Detect per-recipient RCS capability

Decide "RCS-capable" per recipient, not per audience. Two surfaces answer the question:

**Reach scan (bulk pre-check).** `POST /api/v1/rcs/reach-scan` samples a segment against your bot and returns `reach_percent` plus a `recommended_channel` of `rcs`, `mixed`, or `sms` — the answer to "is an RCS-first ladder worth it for this audience" before you touch the campaign. See [the RCS channel page](/channels/rcs#reach-scan).

**Route preview (per recipient).** `POST /messages/route-preview` scores the channel against the recipient's capability flags; pass `rcs_available: true` and RCS can win the pick:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/messages/route-preview" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "body": "Your order #8421 shipped — track it here.",
    "message_type": "marketing",
    "urgency": "low",
    "rcs_available": true,
    "country": "US"
  }'
```

Response — 200:

```json theme={null}
{
  "data": {
    "channel": "rcs",
    "reason": "Rich content for marketing",
    "fallback": "sms",
    "fallbackChain": ["sms"],
    "estimatedCostCents": 2,
    "engagementScore": 0.74
  }
}
```

A recipient with `rcs_available: false` (or with the flag absent, which the router reads as "unknown") never enters RCS into the candidate set — the preview comes back `channel: "sms"`. Where does the flag come from? Your contact store: run the [capability check](/channels/rcs#capability-check) (`GET /api/v1/rcs/capability/:botId/:to`) when a contact enters the segment, cache the verdict for 24 hours, and stamp `rcs_available` on the preview call from that cache. Run the preview over a sample of the audience before committing the ladder — if the sample resolves mostly to SMS, the RCS-first ladder buys you nothing.

## 3. Author the RCS-first campaign ladder

Flip the campaign from SMS-only to a two-hop ladder: RCS as the primary (`order: 0`, must equal the top-level `channel`), SMS as the terminal hop. Channel-specific fields stay on their own entry — the RCS hop carries its template, the SMS hop carries the plain body:

```json theme={null}
{
  "channel": "rcs",
  "channels": [
    {
      "channel": "rcs",
      "order": 0,
      "fallback_on": "failed",
      "fallback_after_seconds": 120,
      "rcs_template": { "name": "order_shipped_card_v3" }
    },
    { "channel": "sms", "order": 1 }
  ],
  "name": "Order shipped — RCS upgrade pilot",
  "type": "blast",
  "audience_type": "list",
  "audience_id": "list_recentBuyers",
  "message_template": "Your order #8421 shipped — track it here. Reply STOP to opt out."
}
```

Reading the ladder entry by entry:

* **The RCS hop fires first.** It can carry either a raw rich payload (`rich_card` / `rich_card_carousel` shape from [the RCS channel page](/channels/rcs)) or an `rcs_template` reference to an approved template from the [Templates tab](/guides/rcs-templates-tab) — never both, and `rcs_template` is legal only on the `rcs` entry.
* **The SMS hop is terminal.** It carries no `fallback_on` / `fallback_after_seconds`; the platform rejects a chain whose last entry still declares a trigger. SMS sends from your normal sending number with normal opt-out screening.
* **The advance trigger decides who stays on RCS.** `fallback_on: "failed"` moves a recipient to SMS only on a terminal RCS failure (`undelivered` / `rejected`, including the capability check returning `RCS_NOT_SUPPORTED`). `fallback_on: "no_engagement"` would also move delivered-but-ignored recipients to SMS — a second paid send per recipient — so keep the trigger on `failed` for an upgrade and measure engagement on the RCS hop instead.

Recipients the capability probe disqualifies never consume the RCS hop's full attempt: the send short-circuits, the hop records `RCS_NOT_SUPPORTED`, and the ladder advances them to SMS.

## 4. Validate in sandbox

Prove the ladder with sandbox keys (`dv_test_sk_*`) before spending a live credit. Two tools cover it:

**Magic numbers.** The sandbox simulator keys delivery outcomes on the recipient's trailing digit — send the campaign draft against recipients ending in `2` (delivered) and `3` (undelivered). A recipient ending in `3` fails the RCS hop terminally and exercises your SMS advance; one ending in `2` settles on RCS and the ladder must stop. The full digit table is in [Sandbox magic numbers](/sandbox/magic-numbers) — it behaves identically across RCS and SMS hops, so each hop's outcome is independently controllable.

**Route preview.** Preview is sandbox-safe by construction (nothing ever sends, even on live keys): re-run the section 2 call with your sandbox key and the magic recipients, and confirm an undelivered-pattern recipient still resolves RCS as the pick — the *delivery* outcome is what advances the ladder, not the pick.

## 5. Read per-hop DLRs

Every hop is its own message with its own receipt. The unified read is `GET /api/v1/notify/:notifyId` — one receipt that lists each hop with its channel, outcome, `error_code`, and billed `price`. For campaign-driven cascades the same per-hop breakdown renders in the dashboard's **Track a cascade** panel (Messages → Multi-channel notify); paste the notify id or the campaign's hop ids. Excerpt of a settled two-hop envelope where RCS failed and SMS delivered:

```json theme={null}
{
  "notify_id": "notify_2kR8wQv1mN",
  "status": "delivered",
  "hop_count": 2,
  "hops": [
    {
      "hop": 0,
      "channel": "rcs",
      "status": "failed",
      "error_code": "RCS_NOT_SUPPORTED",
      "price": 0.0000,
      "currency": "USD"
    },
    {
      "hop": 1,
      "channel": "sms",
      "status": "delivered",
      "error_code": null,
      "price": 0.0084,
      "currency": "USD"
    }
  ],
  "delivered_channel": "sms",
  "cost": { "total": 0.0084, "currency": "USD" }
}
```

Read three things off the receipt after your first pilots:

* **`error_code` mix on the RCS hop.** `RCS_NOT_SUPPORTED` means the recipient was never RCS-capable — a signal to fix your capability flagging (step 2), not the ladder. Carrier rejections mean template or sender issues instead.
* **`delivered_channel` distribution.** The share of recipients settling on SMS is your true RCS reach in the wild; compare it against the reach scan's estimate.
* **`cost.total` vs the SMS-only baseline.** A `failed` RCS hop that short-circuits on capability is free; an RCS hop that attempts and fails still bills. If the terminal-SMS share is high and RCS attempts are costly, restrict the audience to capability-verified recipients.

The full receipt vocabulary (hop ordering, `fallback_of` linkage, cost caps) is in [Track a live cascade by notify id](/guides/track-a-cascade).

## 6. Rollout gates

Do not flip the whole audience on day one. Run the pre-flight, pilot a slice, then widen:

1. **Pre-flight the chain.** `POST /api/v1/campaigns/{id}/preview` now probes every fallback step, not just the primary — for this ladder it verifies the RCS hop has a verified brand agent and the SMS hop has a dedicated sending number. A step that reads unconnected suppresses the green `ready_to_launch`. Details: [Pre-launch readiness for fallback chains](/guides/campaign-fallback-readiness).
2. **Pilot a slice.** Launch the ladder against a small segment (or a versioned audience slice) and read the per-hop receipts from step 5.
3. **Widen or narrow by what the receipts say.** A high `RCS_NOT_SUPPORTED` share means your capability flags are stale — shorten the cache window before widening. A healthy RCS-delivered share means widen to the full audience.
4. **Keep SMS in the ladder forever.** Every advance trigger keeps an exit to SMS; removing it strands non-capable recipients with a hard failure.

## 7. Limits and exclusions

* **Approved brand + agent first.** An RCS hop in any ladder fires only for a verified or launched agent; the campaign pre-flight flags a draft agent as unconnected. There is no sandbox bypass around the approval.
* **Voice never joins the ladder.** `voice` and `fax` are filtered out of cascade chains — OTP step-up via a Verify profile is the only surface that admits voice, and that is a different plane.
* **Org-level fallback is the wide net.** [The RCS channel page](/channels/rcs) also offers an org-wide `RCS → SMS` chain under **Settings → Channels → Cross-Channel Fallback** that applies to every RCS send without a campaign ladder. A campaign ladder overrides it for that campaign; leave the org chain armed as the backstop for direct API sends.
* **Templates gate separately.** An `rcs_template` hop sends only after that template is approved — a `PUT` to the template resets approval to `pending`, so publish template edits before the campaign depends on them.

## Where to go next

* [RCS channel page](/channels/rcs) — capability check, org-level fallback, reach scan, templates.
* [Fallback chains](/guides/fallback-chains) — the full plane map across campaigns, smart-send, and Verify.
* [RCS onboarding](/guides/rcs-onboarding) — brand → agent → verify → launch.
* [Track a live cascade by notify id](/guides/track-a-cascade) — the per-hop receipt surface.
