Skip to main content

Troubleshooting: RCS undelivered or never reaching an RCS-capable device

RCS fails at two distinct layers, and the fix is different for each. This page separates them first, then decodes each cause and its resolution.
  • RCS not supported (capability failure, pre-submit) — the recipient’s handset cannot receive RCS from your agent, so Orbit short-circuits the send before it reaches a carrier. The row fails closed with an RCS_NOT_SUPPORTED receipt. This is a reachability problem, not a delivery problem — fix it before the send.
  • RCS undelivered (carrier-side lifecycle failure) — the agent and the recipient both passed the capability check, but the carrier rejected the message, the agent was not launched, the template schema was refused, or the per-tenant throughput cap turned the send back with a 429. The row lands at undelivered, failed, or rejected with an RCS-specific classified_error_code.
Work the symptom map below, then match the cause to its fix. If the row is stuck pre-queue (queued, pending), work the queue-side troubleshooting page instead; if it holds at sent with no terminal outcome, see Troubleshooting: message sent but no delivery receipt. This page assumes those queues are clear.

Symptom map

Read the status and the classified error code together — the same status word means something different on RCS than it does on SMS. Three places read the same RCS row state: the Delivery Log channel filter (/messages/delivery-log?channel=rcs, with the same status= and direction= vocabulary as every channel), the authoritative row on GET /api/v1/messages/:id, and the message.failed webhook payload with channel: "rcs" for event-driven handling — branch on the payload’s status and error fields, not the event type alone.

Causes and resolutions

Work each cause once, in this order — they are sequential gates, and a higher gate masks the lower ones.

1. Unregistered RCS agent — rcs_business not verified

RCS is a verified-sender channel. The agent must clear four gates — brand submitted, agent created, carrier-verified, launched — before a live send reaches a subscriber. If the agent is in draft or pending verification, the send fails closed. Resolution: work the RCS onboarding gates in order — the RCS channel page checklist compresses them to the four decisions. A brand that was refused or suspended blocks agent creation; fix the brand record first. Verify the rcs_business surface is verified on the Brand Identity trust score before re-sending.

2. Recipient device lacks RCS support

RCS reaches Android devices with Google Messages only. An Apple device, an older Android with no RBM enrollment, or a carrier that does not bridge to the RBM hub short-circuits the send with RCS_NOT_SUPPORTED. Resolution: the pre-send capability check GET /api/v1/rcs/capability/:botId/:recipient tells you whether a number is reachable before you spend the send. When it returns isCapable: false with fallback: "sms", route the message down the SMS path in the same decision. To make this automatic, enable the org-level cross-channel fallback chain Primary: RCS → Fallback: SMS — the router advances on the RCS_NOT_SUPPORTED receipt without you branching per send. A reach scan on a segment (POST /api/v1/rcs/reach-scan) tells you the share of a contact pool that is RCS-capable before a campaign.

3. Template missing a required card/button schema field

Rich sends fail when the payload violates the RCS schema — most often a rich_link_action missing its required fallback_url, a single-card carousel (which renders malformed on Android — use rich_card for one card), or a suggestion chip that is neither a reply nor an action. Plain-text sends on the same agent pass while rich sends fail. Resolution: fix the payload against the schema rules on the RCS channel page — the mutually exclusive shapes (body / rich_card / rich_card_carousel) and the per-chip reply | action contract. When the Brand Identity trust score verifies the agent but rich sends still fail, re-read the send body’s schema before escalating — a schema error is deterministic, not a carrier gate.

4. Agent rejected after onboarding

A launched agent can come back rejected or suspended on a carrier — typically a policy takedown, a quality downgrade, or a carrier-side re-review. The send fails with an RCS-specific classified error; plain and rich sends fail alike. Resolution: read the per-carrier map on GET /api/v1/rcs/bots/:id/quality. A rejected or suspended entry means the carrier revoked the agent — fix the brand/agent record and resubmit through the RCS onboarding guide rather than resending against the same carrier. For a staged launch across a subset of carriers (carrier_mccmnc), a subscriber on a not-yet-launched carrier fails even when the agent is fine elsewhere — check the carrier_statuses map before you blame the content.

5. Quota / throughput — per-tenant cap turned the send back

RCS throttles outbound calls per tenant against the contracted Dotgo RBM Hub throughput. Exceeding the cap turns the send back with a 429 and a retry_after window; retries inside the window hit the same cap. Resolution: the cap is a per-tenant ceiling, not a global pool. If every send on the tenant comes back at once, queue and retry after the retry_after interval rather than hammering the window — the RCS channel page rate limits covers the window mechanics. The operator raises the ceiling (env knobs, DEVOTEL_RCS_RATE_LIMIT per DEVOTEL_RCS_RATE_WINDOW_MS window) to match the contracted Dotgo throughput tier — a per-tenant adjustment, not a code change. A higher contracted Dotgo TPS tier is the durable fix when the ceiling is routinely reached.

What not to do

  • Do not spam retry on a capability failure. RCS_NOT_SUPPORTED is deterministic — the same handset fails the same way every time. Enable the org-level fallback chain or drop the recipient to SMS; do not POST /api/v1/messages/:id/retry against an incapable device.
  • Do not assume RCS ≈ SMS. The verified-sender chain (brand → agent → verify → launch) and the per-recipient capability probe are RCS’s own gates, absent from SMS. Treating RCS as “SMS with a logo” skips both.
  • Do not treat a rate-limit 429 as a carrier failure. The per-tenant cap is your own contracted ceiling — queue and retry after retry_after, and raise it with the operator when the ceiling is routinely reached. Opening a carrier ticket on a self-inflicted cap wastes the trace.

When to escalate

Open a support ticket when one of these holds:
  • The agent shows verified and launched on the target carrier, the recipient’s capability probe returns isCapable: true, the payload matches the schema, and the row still terminates with an unexpected classified_error_code the cause table does not explain.
  • A fleet of carrier-side failures on one destination that persists after a launched agent and a clean capability probe — the delivery log channel=rcs filter plus the deliverability analytics top-N failure-reason breakdown is the aggregate view to quote.
  • You need the provider-side trace against the RBM aggregator.
Include all three so support can pull the RBM pipeline trace without a back-and-forth:
  • Your tenant ID (dashboard under Settings → Organization, or organizationId on GET /api/v1/me).
  • One message ID (msg_…) of a representative failed RCS row.
  • The RCS-specific classified_error_code off that row — read it from metadata.classified_error_code on GET /api/v1/messages/:id, not the display label.

See also