Troubleshooting: CHANNEL_UNAVAILABLE and fallback chain exhaustion
CHANNEL_UNAVAILABLE surfaces in two casings. A 422 means the routing
configuration itself stops the send — no sender or provider can fix it from
the outside. A 503 means the platform attempted your channels and every
route it tried came back unavailable — either nothing is configured to fall
back to, or the ladder was walked to its end. Both casings are tenant-owned:
the fix lives in your channel configuration, your fallback map, or your
send pacing — not in a provider ticket.
This page maps the error to the hop that failed, the reason the ladder ran
out, and the control you own to clear it.
What the error means
The router resolves a send in two passes:- Configuration pass (422). The channel is not connected on your account, no hop in your fallback chain is eligible, or a campaign pre-flight gate (a RED WhatsApp quality rating) aborts before any provider is contacted. Retrying unchanged replays the same refusal.
- Attempt pass (503). The router walked every provider on the primary
channel, then every channel your fallback map lists, and each came back
unavailable. The failure is transient (a circuit breaker skipped the
provider for a window) or real (a genuine outage) — the response tells
you which through
details.transient_skip.
The three operation classes that surface it
The same code surfaces on three send paths. Triage the one you hit:Per-channel ladder
Each primary channel has its own ladder shape. The verify chain is the only one with a platform-fixed order.SMS and WhatsApp (org fallback map)
Your org-level cross-channel fallback map (Settings → Channels) lists, per primary channel, the ordered channels to try when the primary fails. Example: a chainrcs → ["sms", "email"] sends on RCS, retries the body
on SMS if RCS fails, and ends on email if both fail. The adaptive cascade
may re-rank a chain like [whatsapp, rcs, sms] by segment-learned
delivery outcomes before the first attempt — so the order you configured
is the default, not necessarily the order executed.
A cascade-armed send (POST /notify) exhausts like this: RCS fails
(carrier-level), the walk falls to SMS (carrier-level failure again), then
to email, and once email also fails the walk terminates with
fallback.chain_exhausted on the fallback metric and 503 to the caller.
Verify factor chain
The verify chain is platform-ordered SMS → voice → email and is not editable from your fallback map. A verify send that fails on SMS automatically falls to voice, then email; when all three are unavailable thePOST /verify/start call returns 503 CHANNEL_UNAVAILABLE. The same
details.transient_skip flag tells you whether the router skipped the
providers (a breaker window — retry safe) or genuinely failed each one.
Edit the fallback map in Settings → Channels
Fix the chain, not the symptoms:- Open Settings → Channels in the dashboard.
- Find the Cross-channel fallback section and edit the channel that fired — add a hop (the ordered list gains a tail), remove a stale hop, or reorder the list so a reachable channel is tried earlier.
- Save. The per-channel cache invalidates immediately — your next send walks the new chain.
GET /api/v1/settings/channels-fallback and
PUT /api/v1/settings/channels-fallback: send a JSON object keyed by
primary channel whose values are ordered lists (e.g. { "rcs": ["sms"] });
pass an empty array to disable fallback for that primary explicitly.
Self-fallback entries and duplicates are dropped server-side.
A second, per-campaign shape exists: an omnichannel campaign’s channels[]
array is an ordered ladder that layers on the org map with
per-hop fallback_on triggers and fallback_after_seconds windows. The
org map fires when no ladder is configured, or when the ladder’s own
triggered hops all fail.
The details.transient_skip flag — when retry is safe
Every 503 carries a details.transient_skip boolean. Branch on it before
you page anyone:
transient_skip: true— every provider on the channel was skipped because its per-tenant circuit breaker was open or the channel was rate-limited in the window before your send. No provider was contacted — nothing to fix, and the breaker clears on its own. Retrying the send is safe and is what the flag exists to signal: hold a short backoff (the cooldown window) and resubmit.transient_skip: false— at least one provider was attempted and genuinely failed. A retry transmits the same failure unless the upstream fault cleared in the meantime. Investigate before retrying: if every chain hop on every send for a channel fails withtransient_skip: falsefor more than a few minutes, the outage is real — open a ticket only after you’ve ruled out your own fallback map and sender configuration.
transient_skip: true rejections on the same channel
within the same minute are one breaker event; a second distinct channel
failing in the same window moves the diagnosis from your map to the
provider layer.
What to capture before escalating
Work the ladder mapping first. If a send still reads as genuinely unavailable many minutes after the breaker window should have cleared — or a 422 keeps firing after you added a valid hop — open a ticket with:- The error envelope — code, HTTP status, and the full
detailsobject (transient_skip,last_error, any per-hop attribution). - The send path — single message via
POST /messages, a notify cascade, a campaign, or a verify spawn. - The primary channel and the chain as currently configured — the
GET /settings/channels-fallbackbody verbatim. - Your organization ID — Settings → Organization, or
organizationIdfromGET /api/v1/me.
What not to do
- Do not retry a 422. The refusal re-fires identically until the configuration changes.
- Do not retry a
transient_skip: true503 in a tight loop. Back off within the breaker cooldown, then retry once the window likely cleared. - Do not treat
transient_skip: falseas a provider ticket before checking your own fallback map. An empty map ends the chain regardless of provider health. - Do not add the failing channel to its own fallback list. A hop retrying the channel that just failed re-runs the same failure; the platform drops self-fallback entries server-side regardless.
See also
- Cross-channel fallback concept — the org-level chain and the campaign ladder this page triages, in full.
- Fallback and cascade planes — the five fallback planes and how to choose one.
- Fallback chains guide — configure the chain per send and read the walk.
- Verify fallback chains guide — the platform-ordered SMS → voice → email chain.
- Troubleshooting: message undelivered or failed — the DLR-side triage when attempts reach a carrier and the receipt still reads failed.
- Troubleshooting: messaging pre-send gates — the pre-send gate chain that refuses before the router is consulted.
- Troubleshooting: verify factor suite — the factor-side symptom map when the failure isn’t the channel chain.
- Error codes reference — the catalog this page triages.