Skip to main content

Troubleshooting: SMS to a landline skipped before sending

Your Delivery Log shows an SMS row with status undelivered, the error code LINE_TYPE_FILTERED_LANDLINE, and no charge: the row carries is_billable: false and your wallet was not deducted. In the dashboard the row’s error card reads Landline number with “The destination is a landline and can’t receive SMS.” This is not a carrier failure. Orbit’s pre-send line-type filter classified the destination as a fixed line before dispatch and skipped the send, so you never pay for an SMS a landline physically cannot receive.

What the filter is

When the filter is enabled for a send (below), Orbit classifies the destination’s line type with an HLR-backed Number Lookup verdict before the message is handed to a provider. If the verdict is a fixed-line class (fixed, landline, fixed_line, or fixed_voip, depending on how the HLR vendor labels the line), the send is skipped and the row is written as a terminal undelivered with LINE_TYPE_FILTERED_LANDLINE. Mobile numbers, unclassifiable numbers, and plain VoIP numbers pass through: many VoIP DIDs are SMS-capable, so VoIP is deliberately allowed. The check applies to SMS only. It mirrors the landline pre-skip protection Twilio and Sinch apply on their messaging paths, and it reuses the same Number Lookup data that Number Lookup exposes to you. Verdicts are cached for 24 hours, so repeat sends to the same number do not pay for another HLR query.

Enabling and disabling it

The filter is off by default. You opt in per send by setting "line_type_filter": "true" in the send request’s metadata:
To stop filtering, omit the flag (or send any value other than "true") and the send behaves as if the filter were not there. Do not look for an org-wide SMS toggle. The line-type policy slots under Settings → Security (landline_sms, voip_sms, and their voice siblings) govern Verify sends only, and their refusal shape is a 422 VERIFY_LINE_TYPE_BLOCKED at send time, not this code. The two gates are separate: The Verify gate is covered in the FAQ entry for 422 VERIFY_LINE_TYPE_BLOCKED.

What the skip means for the row

  • Terminal undelivered, not failed. The send closed cleanly before dispatch; nothing erred on the send path. The row still fans out on the message.failed webhook like every terminal outcome, with status: "undelivered" and error_code: "LINE_TYPE_FILTERED_LANDLINE".
  • No charge. is_billable is false and no wallet deduction happens. That is the point of the filter: the classic landline-SMS failure mode is a send that bills you and then bounces at the carrier or dies silently.
  • Retrying is not useful. Resending the same row runs the same classification and gets the same verdict back, served from cache. Resend only after you have confirmed the line type and concluded the destination is SMS-capable after all.

When the filter lets a landline through, and when it holds a ported number

The filter is best-effort by design:
  • Lookup outages fail open. If the HLR lookup errors or times out, the send proceeds as if the filter were not there. An HLR outage never blocks your SMS pipeline; you simply lose the billing protection for those sends.
  • The 24-hour cache lags ports. If a number was ported between mobile and fixed, the cached verdict keeps applying until its 24-hour TTL expires. A recently ported landline can therefore stay skipped for up to a day after the port. The practical remedy is to wait out the cache window, and to confirm the current verdict with POST /api/v1/numbers/lookup (request the line-type field) before you resend. There is no customer-facing cache-invalidate switch; the cache expires on its own.

What not to try

  • Do not retry the same rows at scale expecting a different verdict. The classification is deterministic per number and cached for 24 hours; a bulk replay produces a bulk pile of identical undelivered rows.
  • Do not set the flag expecting it to block mobile or VoIP destinations. The filter only skips the fixed-line classes listed above; mobile, unknown, and VoIP pass. To stop sending to specific numbers, use your opt-out or suppression list instead.
  • Do not trace the row with the carrier as a reject. Nothing was submitted; the carrier never saw the message.

Escalation checklist

Open a support ticket when a destination you have confirmed as SMS-capable keeps getting skipped, and include:
  • Your tenant ID (Settings → Organization in the dashboard, or organizationId from GET /api/v1/me).
  • One message ID (msg_…) of a representative skipped row.
  • The line-type verdict for that destination from POST /api/v1/numbers/lookup (line-type field), plus the raw HLR line_type from the row’s route trace (GET /api/v1/messages/:id/trace) where it is visible.

See also