Skip to main content

Troubleshooting: sender and asset preflight gates

Four codes sit between your request and the carrier boundary, and all four refuse before anything queues, submits, or bills. Group them under one mental model: a sender or asset preflight gate judged the request against what that sender or asset can legally do, and the fix lives on your side — in the payload, the sender you picked, or a filing you have not finished.
None of these is retriable as-is. Each refuses deterministically before a queue or a carrier fan-out, so a retry loop earns the same refusal — and NOT_SMS_CAPABLE is explicitly classified as a permanent send error that retry suppression will not attempt again.

Match the channel, then the gate

Pick the lane first — the four codes split two ways.
MMS_NANP_ONLY and NOT_SMS_CAPABLE fire on the send path against the payload and sender you chose. TFV_LINT_BLOCKED and LOA_NOT_SIGNED fire on an asset’s lifecycle endpoint — a toll-free-verification submission and a hosted-messaging order submit — and have nothing to do with the message transport until the asset clears.

Per-code cause table

MMS_NANP_ONLY — MMS outside North America

MMS as a media format only survives NANP carriers (US +1, Canada +1, and the extended-NANP islands). To any other destination the carrier drops the payload silently — and the wallet would still have been charged for the hand-off. The gate rejects the send at the API service layer and at the provider boundary, so the charge never happens. The error body names the recipient:
The fix is never “retry” — decide what the recipient can receive:
  1. Drop the media and send SMS. For non-NANP recipients, remove media_urls (and any MMS-only field) and let the body go as a plain SMS. The message cascade was built for exactly this fall-back.
  2. Keep MMS for NANP recipients only. Segment your audience on the destination prefix (+1) and send the rich payload only to that segment.
  3. Route through a channel that carries media to the destination. RCS and WhatsApp carry media to non-NANP recipients; MMS does not.

NOT_SMS_CAPABLE — the sender cannot carry SMS

A number has capabilities: sms, mms, voice, fax. A voice-only or fax-only number you handed to an SMS send has no sms flag, and the gate refuses rather than letting the dispatch fail downstream. The code is in the platform’s permanent-send-error retry suppression set — no retry attempt changes the outcome, and the response deliberately fails the recipient instead of burning retry budget. Two ways to stay ahead of it:
  1. Pre-check the sender’s capabilities. Read GET /api/v1/numbers/country-capabilities before you map a sender to a country, and confirm the sender you picked advertises an sms capability for that destination’s line type in the response.
  2. Inspect the number once you’ve bought it. Open Settings → Numbers in the dashboard and confirm the sms capability is on the row before wiring the number into a campaign or a sender pool.
The code is deliberately retained even where the live route emits a generic capability refusal — SDKs match on it as part of the permanent retry-suppression contract, so treat a NOT_SMS_CAPABLE response as terminal and fix the sender, not the schedule.

TFV_LINT_BLOCKED — toll-free filing refused by the content lint

The Toll-Free Verification submit (POST /api/v1/numbers/:id/tfv) runs a pre-submit content lint before the filing ever reaches the carrier-side TFV endpoint. The lint matches the sample messages against the well-known carrier disallowed-vertical list (SHAFT categories, payday loans, gambling, crypto promotion, debt collection, lead-gen, get-rich-quick, and phishing-shaped language). A match at error severity blocks the submission — catching a structural rejection in minutes instead of the 5+-business-day carrier review bounce, and keeping you off the 7-day amend window that starts counting the moment the carrier refuses.
The recovery path is a lint-in-edit loop, not a resubmit-and-pray:
  1. Read error.details.lint.findings — each entry names the sample, the pattern it matched, and a suggestion.
  2. Fix the flagged sample messages (or narrow your use case if the traffic legitimately falls in a regulated vertical), then test the fixed samples against POST /api/v1/numbers/tfv-lint. That endpoint runs the same lint without submitting anything.
  3. Once the lint returns ok: true, re-submit POST /api/v1/numbers/:id/tfv with the fixed samples.

LOA_NOT_SIGNED — hosted message order missing its LOA

Hosted messaging (“Hosted SMS”) text-enables a landline or a toll-free number that someone else owns the voice on: you keep the number’s voice traffic on its current carrier, and Orbit takes over the SMS. The mechanism is a Letter of Authorization (LOA) the carrier requires — and orders sit in a lifecycle: draft → loa_signed → submitted → active. The submit endpoint refuses to move an order that skipped the signature step:
The fix is the in-platform signature step, then re-submit:
Then re-submit the order — the same POST /api/v1/numbers/hosted-messaging/<order-id>/submit call succeeds once loaSignedAt is stamped, and the order moves to submitted. The full lifecycle guide is at Hosted messaging.

What not to do

  • Do not retry the same send against a gate. Every one of these refuses deterministically before queue or carrier fan-out; a retry loop earns the same 422/409 and burns rate-limit budget on a decision your side must change anyway.
  • Do not drop the media and silently re-send the same MMS payload. MMS_NANP_ONLY names the recipient: cascade to SMS for that recipient, keep MMS on NANP, or route through a media-capable channel (RCS, WhatsApp) — but a verbatim re-send is just the same refusal again.
  • Do not treat NOT_SMS_CAPABLE as transient. It is in the permanent retry-suppression set by design: no retry will move the outcome, only a sender change will.
  • Do not re-submit an unchanged TFV filing. The carrier would have bounced the same samples the lint flagged; the lint’s judgement is the preview, not the problem.

Paste these into a support ticket

Pick from the block below when the fix above cannot land (a stuck TFV filing, a capability mismatch you cannot see on your Numbers page, an LOA that looks signed but still 409s):