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

# Send your first RCS rich message end to end

> Take one rich card from template approval to a delivered receipt: check the tester device's capability, send through your launched agent, catch the delivery events over a webhook, and fix the three failures that precede a first RCS send.

# Send Your First RCS Rich Message End to End

Onboarding an RCS agent ends at the launch gate — the agent is live, but no message has left it yet. This guide closes that gap with one minimal walk: author and approve a rich-card template, prove the tester device can receive it, send it through your agent, and watch the delivery receipt land on a webhook. Everything here is the single-send version of campaign-scale work; when you scale to a segment, the [RCS campaign playbook](/guides/rcs-campaign-launch-playbook) takes over.

If you have not launched an agent yet, run the [RCS onboarding guide](/guides/rcs-onboarding) first — the brand → agent → verify → launch gates happen there, and this walk fails its first send until the agent is `launched`.

## 1. Prerequisites

* **A launched agent (bot).** The agent must show `launched` on at least one carrier in `GET /api/v1/rcs/bots/:id/quality`. If you are still evaluating without a full brand path, the built-in test account from the [RCS onboarding guide](/guides/rcs-onboarding) substitutes for this walk.
* **A tester device on the agent's tester list.** While the agent is still pre-launch, only registered tester handsets can receive — `POST /api/v1/rcs/bots/:id/devices` (direct registration) or the tester-invite flow. Keep the E.164 number you registered; it is the recipient for every step below.
* **An API key.** Copy it from the dashboard; every call below carries it as `X-API-Key`.

## 2. Author and approve a template

Rich sends reference a stored template that carriers screen for content before it can leave the platform. Author the definition, submit it for review, and wait for the `Approved` badge before you send.

The visual way to do this is the **Studio template editor** on **Messages → RCS → Builder** — pick the rich-card template kind (`card` with a `title`, `description`, and a `mediaUrl`); the [Rich Card Studio guide](/guides/rcs-rich-card-builder) walks the editor end to end. Here, to keep the walk short, create the same rich card directly over the API:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/rcs/bots/bot_4kqzx1/templates" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "rich_template_data": {
      "name": "first_card",
      "title": "Your order shipped",
      "body": "Package is on the way — goodbye, guessing.",
      "media_url": "https://cdn.acmeretail.com/rcs/first-card.png"
    }
  }'
```

The response carries the stored template with an `rcs_tpl_...` identifier and a `pending_review` status. Fetch `GET /api/v1/rcs/bots/:id/templates/:name` until it reads `Approved`; the [Templates tab](/guides/rcs-templates-tab) shows the same badge inline with the deliverability counters. A `PUT` after approval resets the template to pending, so treat approval as a final gate before the walk continues.

## 3. Check the recipient before you send

RCS capability is per device and per agent — a recipient capable for Agent A can be unreachable for Agent B. Probe before sending:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/rcs/capability/bot_4kqzx1/+14155550123" \
  -H "X-API-Key: dv_live_sk_..."
```

Response — 200:

```json theme={null}
{
  "data": {
    "isCapable": true,
    "universalProfileVersion": "3.0",
    "featureFlags": { "mlsSupported": true, "richLinkSupported": true }
  }
}
```

Gate on `isCapable: true`; a `false` (with `fallback: "sms"`) means the handset is not RBM-reachable through this agent and you should send the SMS copy shown in the [RCS fallback and capability concept page](/concepts/rcs-fallback-and-capability) — a skipped probe lands the send as a terminal `RCS_NOT_SUPPORTED`, the failure this walk exists to avoid.

## 4. Send the rich card

With the template approved and the recipient confirmed capable, send one message:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/messages/rcs" \
    -H "X-API-Key: dv_live_sk_..." \
    -H "Content-Type: application/json" \
    -d '{
      "to": "+14155550123",
      "rich_card": {
        "card_content": {
          "title": "Your order shipped",
          "description": "Package is on the way — goodbye, guessing.",
          "media": {
            "height": "MEDIUM",
            "content_info": { "file_url": "https://cdn.acmeretail.com/rcs/first-card.png" }
          }
        }
      }
    }'
  ```

  ```javascript Node theme={null}
  const res = await fetch("https://api.orbit.devotel.io/api/v1/messages/rcs", {
    method: "POST",
    headers: {
      "X-API-Key": process.env.ORBIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      to: "+14155550123",
      rich_card: {
        card_content: {
          title: "Your order shipped",
          description: "Package is on the way — goodbye, guessing.",
          media: {
            height: "MEDIUM",
            content_info: { file_url: "https://cdn.acmeretail.com/rcs/first-card.png" },
          },
        },
      },
    }),
  });
  if (res.status !== 202) throw new Error(await res.text());
  const { data } = await res.json();
  console.log(data.id); // msg_... — keep this for step 5
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
      "https://api.orbit.devotel.io/api/v1/messages/rcs",
      headers={"X-API-Key": "dv_live_sk_..."},
      json={
          "to": "+14155550123",
          "rich_card": {
              "card_content": {
                  "title": "Your order shipped",
                  "description": "Package is on the way — goodbye, guessing.",
                  "media": {
                  "height": "MEDIUM",
                  "content_info": {"file_url": "https://cdn.acmeretail.com/rcs/first-card.png"},
              },
              }
          },
      },
  )
  assert res.status_code == 202, res.text
  message_id = res.json()["data"]["id"]
  print(message_id)  # msg_... — keep this for step 5
  ```
</CodeGroup>

The response is `202` with the message identifier as `msg_...` — keep it; the webhook events in step 5 key on it. Inline rich cards like the example send instantly; the cleaner production shape sends the approved template by name through a campaign — the [RCS channel page](/channels/rcs) covers every body field, and its `rich_card_carousel` sibling takes 2–10 cards in one send.

## 5. Read the delivery lifecycle over a webhook

A `202` means the message is accepted for delivery, not delivered. Register one receiver and subscribe to the two outcomes that resolve an RCS first-send:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/webhooks" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.example/hooks/orbit",
    "events": ["message.delivered", "message.failed"]
  }'
```

The receiver shape — verify the signature, handle the event, enqueue retries on failure — is covered in the [durable webhook consumer guide](/guides/webhook-consumer). A delivered rich card arrives as a normalized envelope; the events catalog lists the full vocabulary on the [webhook events page](/webhooks/events):

```json theme={null}
{
  "event": "message.delivered",
  "data": {
    "id": "msg_9f3c8a12",
    "channel": "rcs",
    "status": "delivered",
    "delivered_at": "2026-09-23T14:11:02Z"
  }
}
```

RCS also reports `message.read` — the open receipt SMS never produces — so subscribe to it when you are past first-send debugging and want engagement data.

## 6. Troubleshoot the three predictable failures

First RCS sends usually die in one of three ways; each resolves to a specific pre-send gate:

| Code                                   | Cause                                                                                                | Fix                                                                                                                                                            |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INVALID_LAUNCH_STATE` (409)           | The agent is not `launched` on any carrier — onboarding is not through gate 4.                       | Poll `GET /api/v1/rcs/bots/:id/quality` until `carrier_statuses` shows `launched`; sending while `pending_launch` answers this.                                |
| Template not approved (422)            | The template id names a draft — `PUT` after approval reset it to pending, or it was never submitted. | Resolve the template against the agent's roster and confirm the badge reads `Approved` on the [Templates tab](/guides/rcs-templates-tab); re-submit and wait.  |
| `RCS_NOT_SUPPORTED` (terminal failure) | The capability check was skipped and the handset is not RBM-reachable.                               | Gate on `isCapable: true` from `GET /api/v1/rcs/capability/:botId/:recipient` before every send; route `fallback: "sms"` recipients down the SMS path instead. |

For deeper diagnosis — carrier rejections, delivery-dips, capability drift — the [RCS undelivered troubleshooting page](/troubleshooting/rcs-undelivered) reads the classified error code off `GET /api/v1/messages/:id` so you can attach cause to outcome without re-sending traffic.

## 7. Next steps

* Scale the same card to a segment in the [RCS campaign playbook](/guides/rcs-campaign-launch-playbook) — reach scan, ladder fallback, dry-run, and launch are the campaign version of steps 2–4.
* Read the [RCS channel page](/channels/rcs) for carousels, suggested actions, and the fallback chain concept.
* Cover the durable receiver pattern in the [webhook consumer guide](/guides/webhook-consumer) before your first production rollout.
