> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: sender and asset preflight gates

> Four preflight codes block a send or an asset lifecycle step before anything queues or submits: MMS_NANP_ONLY, NOT_SMS_CAPABLE, TFV_LINT_BLOCKED, and LOA_NOT_SIGNED. Match the code to its fix — MMS off a non-North-American recipient, a sender that cannot carry SMS, a toll-free filing the content lint refused, and a hosted-messaging order whose LOA is unsigned.

# 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.

| Code               | Gate                                     | Why it refuses                                                                                                                                                                                               |
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `MMS_NANP_ONLY`    | Channel fit on the send                  | MMS to a recipient outside the North American Numbering Plan (NANP). Carriers outside NANP silently drop MMS, so the platform stops it before your wallet is charged.                                        |
| `NOT_SMS_CAPABLE`  | Sender capability on the send            | The sender you picked cannot carry SMS in the destination country — a voice-only or fax-capable number with no `sms` capability. Treated as permanent: no retry moves the outcome.                           |
| `TFV_LINT_BLOCKED` | Asset lifecycle (toll-free verification) | Your Toll-Free Verification filing tripped the pre-submit content lint — one or more sample messages match a carrier-disallowed vertical. Fix it now, before the carrier review queue bounces it days later. |
| `LOA_NOT_SIGNED`   | Asset lifecycle (hosted messaging)       | A hosted-messaging order can only reach the carrier once the Letter of Authorization is signed. No LOA signature, no submit.                                                                                 |

<Note>
  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.
</Note>

## Match the channel, then the gate

Pick the lane first — the four codes split two ways.

```
What were you doing?

├─ Sending a message (SMS / MMS payload)?
│   ├─ MMS to a non-NANP recipient ............ MMS_NANP_ONLY
│   └─ sender has no SMS capability for the
│      destination country ................... NOT_SMS_CAPABLE
│
└─ Working an asset lifecycle (number/asset compliance filing)?
    ├─ Toll-Free Verification submission ....... TFV_LINT_BLOCKED
    └─ Hosted-messaging order submit ........... LOA_NOT_SIGNED
```

`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

| Code               | Where it fires                                     | Root cause                                                              | Fix                                                                                                             |
| ------------------ | -------------------------------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `MMS_NANP_ONLY`    | `POST /api/v1/messages/sms` (MMS payload)          | `to` resolves outside NANP                                              | Send the body as SMS-only to that recipient and let the cascade fall back, or keep MMS for NANP recipients only |
| `NOT_SMS_CAPABLE`  | any SMS send                                       | sender has no `sms` capability for the destination                      | Pick an SMS-capable sender, or pre-check `GET /api/v1/numbers/country-capabilities` before you map a sender     |
| `TFV_LINT_BLOCKED` | `POST /api/v1/numbers/:id/tfv`                     | sample messages matched a disallowed-vertical pattern at error severity | Fix the flagged samples (`details.lint` in the envelope) and re-submit — the carrier would bounce it anyway     |
| `LOA_NOT_SIGNED`   | `POST /api/v1/numbers/hosted-messaging/:id/submit` | order's LOA was never signed                                            | Sign it with `POST /api/v1/numbers/hosted-messaging/:id/loa/sign`, then re-submit                               |

## `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:

```json theme={null}
{
  "error": {
    "code": "MMS_NANP_ONLY",
    "status": 422,
    "message": "MMS is only supported for North American Numbering Plan destinations. Drop the media payload or cascade to SMS for this recipient.",
    "details": { "to": "+442071838750" }
  },
  "meta": { "request_id": "req_9f3a2b1c", "timestamp": "2026-09-12T14:22:01Z" }
}
```

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](/concepts/message-cascade-groups) 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.

```json theme={null}
{
  "error": {
    "code": "TFV_LINT_BLOCKED",
    "status": 422,
    "message": "TFV samples violate carrier content policy. Fix the highlighted issues before submitting — carrier rejection would cost 5+ business days and burn into the 7-day amend window.",
    "details": {
      "lint": {
        "ok": false,
        "findings": [
          { "sample_index": 1, "severity": "error", "pattern": "payday-loan", "suggestion": "Remove short-term lending language from the sample message." }
        ]
      }
    }
  },
  "meta": { "request_id": "req_4c1d8e2f", "timestamp": "2026-09-12T14:25:41Z" }
}
```

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:

```json theme={null}
{
  "error": {
    "code": "LOA_NOT_SIGNED",
    "status": 409,
    "message": "The LOA must be signed before the order can be submitted."
  },
  "meta": { "request_id": "req_7a0b4f91", "timestamp": "2026-09-12T14:31:02Z" }
}
```

The fix is the in-platform signature step, then re-submit:

```bash theme={null}
curl -X POST \
  https://api.orbit.devotel.io/api/v1/numbers/hosted-messaging/<order-id>/loa/sign \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "signerName": "Alex Morgan", "signerEmail": "alex@example.com" }'
```

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](/guides/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):

```
Error code: <e.g. TFV_LINT_BLOCKED>
Endpoint: <e.g. POST /api/v1/numbers/:id/tfv>
request_id: <from error.meta.request_id>
Timestamp: <UTC, or local time with tz>
Number / asset: <E.164 or order id>
What you changed before the last attempt: <e.g. replaced sample 2>
```

## Related references

* [US toll-free sender blocked with TFV\_REQUIRED](/troubleshooting/toll-free-tfv-required) —
  the sibling gate on set-not-verified toll-free senders; the lint gate
  above in this page is the pre-submit sibling.
* [Hosted messaging guide](/guides/hosted-messaging) — the LOA lifecycle
  end to end.
* [MMS media-rich content guide](/guides/mms-media-rich-content) — the
  channel constraints MMS answers to.
* [Error Code Reference](/reference/error-codes) — the full catalog where
  these four codes sit.
* [FAQ](/reference/faq) — the short-form summary of this gate set.
