Skip to main content

Assign and reassign numbers

An Orbit number is only “assigned” once it terminates somewhere: an AI agent, an IVR, a queue, a ring group, voicemail, a SIP forward, or a webhook. This guide walks the full assignment workflow — where to assign in the dashboard, when the bulk Assignment Portal beats per-number editing, how tracking pools (DNI) interact with assignments, how per-user PBX/SIP registration fits in, and the fallback order the routing engine runs through. Reads need the numbers:read scope; every assignment or routing mutation needs numbers:write. Route targets live in your org’s tenant schema, and all below endpoints are org-scoped — a sibling subaccount cannot wire a number it doesn’t own.

1. Where to assign: per-number detail vs the bulk portal

Two entry points cover the assignment surface. Per-number detail — open a DID’s page at Numbers → pick a number. The routing card on that page edits the inbound route for this one DID (PUT /numbers/{phone}/routing). Use it when you need a single line’s route changed, or when you’re inspecting what a specific number currently answers with.
Assignment Portal (bulk)Numbers → Assignment Portal (/numbers/assignments). One screen: unassigned (and optionally assigned) DIDs in the left column, destinations (AI agent, IVR flow, ring group, queue, voicemail, SIP forward, webhook) in the right column. Drag a DID onto a destination, confirm in the sheet, and the portal fires one PUT /numbers/{phone}/routing per dropped DID. Use it when you onboard a block of purchased DIDs or re-wire a whole team’s lines at once — it replaces the multi-menu per-number loop. Route types the portal and the per-number routing surface accept: agent, softphone_user, softphone_register, ivr, queue, ring_group, voicemail, sip_forward, transfer, conference, shared_line, dispatch_to_voicemail_box, webhook, and decline. The full per-type config catalog is in Phone Numbers.

2. Assignments vs tracking pools (DNI) — what unwinds

Assignment and attribution are two different layers that operate on the same DID.
  • The inbound route on a DID (/numbers/{phone}/routing) says who answers the call.
  • A tracking pool (Call tracking & DNI) says which marketing source the call attributes to. Pool assignment lives in org settings JSONB; route assignment lives on the per-DID inbound_routes row.
When you wire a tracking DID into a pool, keep its inbound route as a fallback: a visitor whose session doesn’t resolve a pool DID (or when the pool is disabled) still hits the DID’s own route. When a tracking pool DNI session un-winds — swap a DID back out of a pool — the underlying inbound route is untouched. Assignment unwinds one layer; the other survives. If an inbound call appears to ignore its queue and ring a temporary tracking number instead, check the pool first: pools override the displayed DID at the attribution layer, not the routing layer, so the call’s target stays whatever the route says. Scope note: pool membership is an org-level attribute; two sibling subaccounts on the same parent cannot hold the same DID in different pools.

3. Per-user PBX/SIP registration

Route type sip_forward (that’s the type value) forwards inbound calls onto a SIP URI — the general-purpose case for BYO-PBX trunks (your console’s own PBX / Asterisk / FreeSWITCH / 3CX), plus the per-user case where individual extensions answer. The full PBX walkthrough (outgoing-direction trunk, transport UDP/TCP/TLS, registration health) is in Register your PBX on Orbit. The assignment step from this guide is: once the PBX trunk is registering healthy, point each DID at that trunk’s SIP URI via sip_forward. Per-user ring behavior — one DID ringing a specific extension — is a softphone_user or softphone_register route, and a shared-appearance group on a department line is shared_line. A DID’s inbound route is ONE target; mixing PBX trunking with app-side destinations means choosing a route per DID. A common pair is a main-line DID routing to a queue and an overflow/reassigned DID pointing at the PBX.

4. Fail-safe fallback order

When a route target is unhealthy or empty, the routing engine walks a deterministic fallback order instead of dropping the caller:
  1. Queue — try the named queue first. Queue membership + skill routing + queue-level business hours run under this step.
  2. Ring group — if the queue has no agents, no agents within the business-hours gate, or a queue-level failure, the ring group the fallback is configured with rings. Group strategy (simultaneous, sequential, round-robin) applies here.
  3. Voicemail — if the ring group also doesn’t answer, the DID’s voicemail (or dispatch_to_voicemail_box for a shared department mailbox) catches the call.
This is the reason an assignment isn’t just “one DID → one destination.” A queue assignment implicitly names its own fallback chain (que_support → ring_group_x → voicemail). Operators designing assignments should set the queue route on the DID and then verify the queue’s configured ring-group fallback exists; the per-DID voice-queues and voicemail-boxes guides cover the group/mailbox side.

5. Conflict scope — when assignments fight

Two assignment surfaces target the same DID; know which one wins.
  • DNI pool vs direct route: pool assignment wins for attribution; the direct route still decides who answers. Never both a pool-bound tracking DID AND a per-DID route you expect to be the “real” answerer — pools are inbound-only and never override the sip_forward/queue target.
  • Reassign across subaccounts (Number Lifecycle): POST /numbers/{id}/reassign moves ownership to a sibling subaccount. The org-level route (/numbers/{phone}/routing) the target org will see is whatever that org wires up itself after reassignment — ownership move and route move are separate steps.
  • decline: a DID set to decline explicitly silences inbound. It’s a deliberate assignment, not “unassigned.”
  • webhook (Twilio voiceUrl parity): a webhook route takes over the whole call’s call-control — assigning a webhook wins over every in-platform destination by design.

6. Re-assignment examples with a dry-run

Reassingments fall into two flavors:

Move one DID’s route

The same call returns 422 when the sip_forward uri is malformed, the queue config carries no queueId, or the ivr flow id is absent — the strict write schema rejects mis-keyed blobs instead of silently persisting a route the runtime can’t interpret.

Reassign ownership to a sibling subaccount

Constraints: target must be a same-tenant, same-family sibling; the caller must currently own the DID; trial-pool claims and numbers you only borrowed can never be reassigned. See Number Lifecycle.

Dry-run strategy

There is no server-side dry_run: true flag on the routing PUT. The safest pre-flight is:
  1. GET /numbers/{id} — confirm the DID is active and carries the capabilities the route expects (a voice route on a sms-only DID will write but never terminate a call).
  2. GET /numbers/{phone}/routing — read the CURRENT route so you know what you’re replacing.
  3. Software-side: run candidates through the strict schema you’ve already got client-side (queue needs queueId; IVR needs flowId; sip_forward needs a uri) before the PUT.
If a destructive bulk change is on the table, run it through the Assignment Portal first — the portal surfaces the current route per DID in its right-column destination cards, so you see exactly what the pending confirm will overwrite.

7. Troubleshooting

See Also