Troubleshooting: messaging pre-send gates
Before any SMS, MMS, or conversational-channel send hands off to the carrier, Orbit runs a short chain of hard pre-send gates. A send rejected at this stage never reached the softswitch — the structured 422/403 envelope names the gate, and each gate defends a different control. This page is the routing card for the four hardest and most visible of those gates: the org-side blocked-contact list, the quiet-hours window, the conversational channel’s session window, and the carrier deactivation scrub. Definitions live in the Error Code Reference; this page only owns the triage path. Distinguish these hard rejects from the campaign-level pre-send verdicts (blocked vs warned) covered on voice pre-send gate chain: the codes below stop a single send at the message or conversation layer, before any campaign list stage runs.Section routing — which gate fired
CONTACT_BLOCKED — recipient on the blocked list
Cause. The contact matched by the send’s to address (phone or email)
is flagged blocked at the organization level, so every outbound send to
that contact is rejected before routing starts. Blocking is one of your
own deliverability controls — typically applied after abuse, a complaint,
or a manual block action in the dashboard.
Read the surface. The 403 envelope carries
details.contact_id, so you can open the exact row.
Fix. Unblock the contact under Settings → Contacts → Blocked and
retry the send. If the block was deliberate, remove the contact from the
campaign audience instead — the gate keeps rejecting until either the row
is unblocked or the send stops targeting it. Bulk audiences hit this one
contact at a time: a blocked member fails its own send while the rest of
the batch proceeds.
Do not confuse with the suppression model —
suppression scope covers
opt-outs that filter imports, and
pending erasure gates
covers CONTACT_ERASURE_PENDING. A blocked contact is the org-side
hard list, distinct from both.
QUIET_HOURS_BLOCKED / TCPA_QUIET_HOURS — the send-time window
Cause. The send landed outside the recipient-local allowed window.
For non-voice channels (SMS / MMS / WhatsApp / RCS / Email / Messenger /
LINE / and the rest) the QUIET_HOURS_BLOCKED gate checks your own
quiet-hours posture under Settings → Compliance → Quiet hours — a
tenant-owned control. Voice carries the TCPA-family codes
(TCPA_QUIET_HOURS, plus the hard federal / state window family) instead.
When the recipient timezone cannot be resolved, the non-voice equivalent
QUIET_HOURS_TIMEZONE_UNKNOWN follows your unknown_timezone_policy
(allow or deny), unlike the voice-side family which fails closed.
Fix. Two ways to clear, both tenant-owned for the non-voice gate:
- Reschedule at the
next_allowed_attimestamp returned in the error envelope — the DST-safe absolute instant the window reopens. - Relax the posture — widen or disable the quiet-hours window in Settings → Compliance → Quiet hours, then retry.
GET /compliance/quiet-hours/preview before launching.
OUTSIDE_SESSION_WINDOW — reply outside the channel window
Cause. A free-form reply went out on a session-based channel —
WhatsApp’s 24-hour customer-service window, Messenger’s 24-hour messaging
window, WeChat’s 48-hour window — after the window closed. The window
opens on the contact’s most recent inbound message and counts down from
it; once it closes, only template (WhatsApp) or tagged (Messenger)
messages go through.
Fix. Pick one of two paths:
- Send a template reply — on WhatsApp, dispatch an approved message template; on Messenger, send with an allowed messaging tag. Both reach the contact outside the free-form window.
- Wait for the window to reopen — every inbound from the contact (message, postback, button tap) reopens the window, and a free-form send then succeeds.
MESSAGING_WINDOW_CLOSED envelope are worked end-to-end on
messenger window closed — this
page only routes the generalized OUTSIDE_SESSION_WINDOW code (which
also covers WhatsApp and WeChat windows) onto those same two recovery
paths.
MESSAGING_NUMBER_DEACTIVATED — recipient churned per the carrier feed
Cause. For NANP (+1) SMS/MMS destinations, Orbit scrubs the
recipient against the carrier deactivation (churn) feed before the send
leaves. The feed reported the number deactivated on or after your most
recent recorded consent grant — meaning the number may be reassigned to a
new subscriber or simply disconnected. Sending would risk delivering to
the wrong person, so the send fails closed with a 422 and an audit-log
entry for compliance review.
Fix. Two valid resolutions:
- Remove the number from the list — the safest path when you have no fresh relationship with that destination.
- Refresh consent and retry — once the contact re-confirms their number (a new recorded grant), the next send’s scrub compares the feed against the newer grant date and passes.
VERIFY_NUMBER_DEACTIVATED
on POST /verify/start — same carrier feed, verify-channel scope.
When to escalate
One of these codes persisting after the fix above is abnormal. Escalate to support with:- Your organization ID — Settings → Organization, or from
GET /api/v1/me. - The exact code and
meta.request_idfrom the rejected envelope. - For quiet-hours: the recipient destination and your current send window settings.
- For deactivation: the destination number and your last recorded consent-grant timestamp for it.
See also
- Error Code Reference — definitions for every code routed here.
- Troubleshooting: message parked before sending —
the non-error parking states (
scheduled,pending,queued) that hold a send before any gate runs. - Voice pre-send gate chain — the campaign-level verdict chain the voice path runs.
- Troubleshooting: TCPA window blocked calls — the voice-side window split this page routes around.
- Troubleshooting: messenger window closed — the session-window recovery ladder (tags, templates) in full.