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

# Inbound message resolution

> How every inbound SMS (MO) or delivery receipt (DLR) resolves to its owning tenant: the number index, the routed number states, the hourly self-healing reconcile, the per-DID route override, and subaccount sharing.

# Inbound message resolution

Inbound message resolution answers one question for every mobile-originated (MO) SMS or carrier delivery receipt (DLR) that reaches Orbit: **which tenant does this belong to?** Resolution runs before any routing rules or inbox assignment — a message that cannot be resolved to a tenant is dropped before anything else sees it. This page explains that ownership step end to end.

Tenant-level routing rules that further distribute a resolved message (keyword or sender match into a webhook, queue, inbox, team, or SMS menu) are covered in [Inbound message routing](/concepts/inbound-message-routing). Everything below is the tenant-ownership layer underneath that rule engine.

## How inbound resolution works

Every inbound MO or DLR carries a destination phone number (your DID) and a sender. Orbit resolves the destination number to its owning tenant in four ordered steps, stopping at the first hit:

1. **Platform inventory.** The destination number is looked up in the platform number index — the inventory of numbers purchased through Orbit that are stamped to a specific organization. Owners of deleted organizations never match; the lookup only resolves to a live organization.
2. **Trial-pool assignments.** Shared trial numbers handed out for time-bounded testing resolve through their active assignment window.
3. **The reverse-ownership map.** A dedicated number index maps a phone number directly to its owner, updated when a number is purchased and removed when it is released. This is the O(1) path that covers direct-carrier purchases and keeps resolution fast at any cluster size.
4. **Bounded fallback scan.** When none of the index paths hit, resolution scans tenant records to find an owner, checking each tenant's own numbers. The scan is deliberately bounded: it refuses to guess when two tenants both claim the number (ambiguous ownership drops the message instead of risking a cross-tenant leak), and when no owner is found the message is discarded as unroutable. It only fires for numbers missing from the index — not for genuinely unowned numbers, which are correctly dropped.

Resolution is a guard, not just a directory lookup: a resolved route whose recorded owner disagrees with current ownership is rejected, and the message falls back to the tenant-wide default. This is what stops a recycled DID — released by one tenant and re-claimed by another — from delivering the new owner's inbound content to the previous owner's webhook endpoint.

## The routed number states

A number only resolves inbound traffic while it is **owned** in one of three states: `active`, `pending_compliance`, or `suspended`. Numbers in any other state — `released`, `parked`, or never purchased — do not own inbound traffic, and messages addressed to them are discarded at the resolution step.

* **`active`** — fully usable; inbound lands normally.
* **`pending_compliance`** — purchased into a regulated country while the compliance bundle verifies. Still owned, so inbound resolution still lands it; the number is never orphaned during verification.
* **`suspended`** — a soft pause (for example an overdue balance). Suspend stops usage, but ownership is retained so inbound resolution continues to route; inbound does not fall into an unowned-number black hole.

Release the number and the reverse-ownership entry is removed, so resolution correctly stops resolving. See [Number status map](/concepts/number-lifecycle) for the full state vocabulary and [Number lifecycle](/numbers/lifecycle) for the transitions between them.

## The hourly self-healing reconcile

Index completeness is not dependent on every purchase-time write succeeding. An hourly background reconciliation scans every live tenant's SMS-capable numbers and re-creates any reverse-ownership entries that drifted — for example when a purchase-time write failed silently, or an out-of-band administrative edit bypassed the normal write path. A one-time drift therefore self-clears within one hour; inbound that spontaneously returns after the next hourly window is the expected self-heal path, and no support action is needed.

There is also a per-message self-healing path: when the bounded fallback scan unambiguously resolves an owner, the reverse-ownership index is backfilled on the spot, so the next inbound message for that number resolves through the O(1) index lookup instead of re-running the scan.

Resolution paths layer this way intentionally: fast indexed lookups handle the common case, the bounded scan catches drift, and the hourly sweep repairs the index so the scan stays rare.

## The per-DID route override

Resolution decides *which tenant* receives the message. The per-DID route override decides *where inside the tenant* the message terminates. Each DID carries an optional route override — the dashboard **route configuration** panel on the number — with an `sms_route_type` of `webhook`, `agent`, `auto_reply`, or `disabled`:

* **`webhook`** — the inbound content is POSTed to your HTTPS endpoint (with an optional secondary delivery target). Use this when your own service consumes inbound.
* **`agent`** — the message is handed to an AI agent for two-way conversation over SMS. Use this when replies should come from an agent rather than a human.
* **`auto_reply`** — one fixed response body (up to 1600 characters) is sent back for each inbound message. Use this for acknowledgment-only numbers.
* **`disabled`** — nothing. The message is resolved into your tenant's inbox but never dispatched anywhere. Voice-style routing for the same DID is unaffected; the toggle is SMS-specific.

Why you must set it deliberately: `disabled` makes inbound look exactly like a resolution failure — the message arrives, resolves, is stored, and then goes nowhere. When a number receives texts but your integration hears nothing, check the override before suspecting resolution. The [inbound troubleshooting flow](/troubleshooting/inbound-sms-not-recording) lists clearing a `disabled` override as the first escalation gate for exactly this reason.

## Subaccounts share resolution

Sibling subaccounts share the parent's tenant schema, so a number bound to one subaccount resolves inbound for the whole organization — the inbound becomes visible at the organization level, not only to the owning subaccount. Cross-tenant boundaries are unaffected: a sibling subaccount can receive your inbound, an unrelated tenant never can. What siblings do and do not share is detailed in [Tenant isolation](/concepts/tenant-isolation).

## Subscription gaps never break resolution

Inbound routing correctness is not gated on billing status. A tenant whose subscription has cancelled keeps its provisioned schema and its SMS-capable numbers, and the hourly reconciliation continues to enumerate and repair it — resolution never checks subscription state. A cancelled-subscription tenant's inbound keeps routing and keeps self-healing; what subscription controls is spend and usage, not the ownership index.

## See also

* [Inbound message routing](/concepts/inbound-message-routing) — the tenant-level rule engine (match types, targets, fall-through) layered on top of resolution
* [Troubleshooting: inbound SMS not reaching your webhooks](/troubleshooting/inbound-sms-not-recording) — the diagnostic flow, including the hourly self-heal window and the `disabled` override check
* [Number status map](/concepts/number-lifecycle) — the six number states and who moves a number into each
* [Number lifecycle](/numbers/lifecycle) — the operations that move a number between states
* [Tenant isolation](/concepts/tenant-isolation) — the subaccount sharing boundary
