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_SUPPORTEDreceipt. 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, orrejectedwith an RCS-specificclassified_error_code.
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 withRCS_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 arich_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 backrejected 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 a429 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_SUPPORTEDis deterministic — the same handset fails the same way every time. Enable the org-level fallback chain or drop the recipient to SMS; do notPOST /api/v1/messages/:id/retryagainst 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
verifiedandlaunchedon the target carrier, the recipient’s capability probe returnsisCapable: true, the payload matches the schema, and the row still terminates with an unexpectedclassified_error_codethe 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=rcsfilter 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.
- Your tenant ID (dashboard under Settings → Organization, or
organizationIdonGET /api/v1/me). - One message ID (
msg_…) of a representative failed RCS row. - The RCS-specific
classified_error_codeoff that row — read it frommetadata.classified_error_codeonGET /api/v1/messages/:id, not the display label.
See also
- RCS channel page — the concept-and-reference surface (capability check, fallback chain, rate limits, onboarding gates)
- RCS onboarding guide — the four-gate walkthrough with every endpoint and the status tables
- Cross-channel fallback — the
org-level chain that turns a
RCS_NOT_SUPPORTEDreceipt into an SMS - Multi-channel DLR fallback — the terminal-DLR plane vs. the capability plane where RCS fails
- Troubleshooting: message undelivered or failed — the generic decoder (all channels); this page covers the RCS slice
- Brand Identity trust score —
the
rcs_businessverified-sender check an RCS agent must clear