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

# Assign and reassign numbers: who owns what, one workflow

> Decide where to assign (per-number detail vs the bulk Assignment Portal), how DNI tracking pools unwind assignments, per-user PBX/SIP registration, and queue→ring-group fallback order. Includes re-assignment API examples with a dry-run strategy.

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

```bash theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/numbers/+14155550100/routing" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "queue",
    "config": { "queueId": "que_support" }
  }'
```

**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](/numbers/overview#configure-routing).

## 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](/guides/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](/guides/registering-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](/guides/voice-queues) and [voicemail-boxes](/guides/voice-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](/numbers/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

```bash theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/numbers/+14155550100/routing" \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sip_forward",
    "config": { "uri": "sip:+14155550100@trunk.example.com;transport=tls" }
  }'
```

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

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/num_abc123/reassign \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "target_org_id": "org_sibling456" }'
```

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](/numbers/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.

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/numbers/num_abc123" \
  -H "X-API-Key: dv_live_sk_..."

curl "https://api.orbit.devotel.io/api/v1/numbers/+14155550100/routing" \
  -H "X-API-Key: dv_live_sk_..."
```

## 7. Troubleshooting

| Symptom                                                                   | Likely cause                                                                | Fix                                                                                                     |
| ------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Inbound call behaves like the queue is down even though agents are online | Queue config exists but queue membership or business hours gate is blocking | Check the queue's business-hours gate and membership before assuming the DID assignment is wrong.       |
| After moving a DID between subaccounts, inbound calls hit old route       | Reassignment changed ownership; the route was not re-set on the new org     | After reassign, run `PUT /numbers/{phone}/routing` under the new org's API key to (re)assert its route. |
| Attribution shows calls on the wrong DID                                  | Tracking pool override                                                      | Remember pools affect attribution, not routing; the DID's inbound route still decides who answers.      |
| `422` on routing write                                                    | Mis-keyed config (`queue` missing `queueId`, `ivr` missing `flowId`)        | The strict schema rejects mis-typed keys — match the field names to the route type you set.             |
| A DID doesn't ring even after assignment                                  | The DID is `decline`d, or it was bought without the `voice` capability      | `GET /numbers/{id}` — check `capabilities` includes `voice`; otherwise the route never terminates.      |

## See Also

* [Phone Numbers](/numbers/overview) — the route-type catalog, capabilities, and the number types.
* [Number Lifecycle](/numbers/lifecycle) — reassign, auto-renew, scheduled release, reclaim.
* [Call tracking & DNI](/guides/call-tracking-dni) — pools that attribute inbound calls to marketing sources without unwinding your routing.
* [Register your PBX on Orbit](/guides/registering-your-pbx-on-orbit) — the PBX/SIP trunk walkthrough before assigning a DID to `sip_forward`.
* [Inbound number routing](/guides/inbound-number-routing) — routing primitives (queues/IVR/agents) the Assignment Portal wraps.
* [Buy and provision numbers](/guides/buy-numbers) — the post-purchase wiring this guide builds on.
