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

# Batch port-in: multi-number lifecycle for one shipment

> Run a multi-number or organizational transfer as one staged shipment: when to batch vs. file per-number, the bulk checklist (CSV shape, LOA, staging), tracking many transfers with webhooks, and the post-cutover provisioning checklist — worked through a 12-number retail migration.

# Batch port-in: one shipment, many numbers

A single-DID port teaches the state machine; a real migration — a retailer moving twelve regional storefront lines, an org folding an acquired carrier account into Orbit — fails on the coordination, not the mechanics. This guide orients a multi-number transfer around the porting dashboard and API as one staged shipment: it picks the right path per numbers, runs the bulk checklist in the right order, tracks the whole batch without polling, and closes out post-cutover provisioning deliberately.

Reads (`check`, `check/bulk`, `pre-validate`, `list`, `timeline`) need the `numbers:read` scope; writes (create, LOA upload/sign/submit, bulk CSV import, cancel) need `numbers:write` and an `owner`, `admin`, or `developer` role. The per-endpoint runbook is [Port a number end-to-end](/guides/port-numbers); this page is the batch-oriented layer on top of it.

## 1. Two lifecycles: per-number goals vs. one shipment

Every number you move still travels the one-directional port state machine — `submitted → reviewing → approved → completed`, with `rejected` and `cancelled` as terminal branches — and the LOA rides on top (`draft → signed → submitted`). A batch changes nothing about that machine; what changes is *how many rows hold it and who dispatches them*:

* **A one-time transfer** moves one number through the wizard or `POST /numbers/porting` with a full dispatch — the carrier order opens when the request lands, and the LOA attaches after.
* **A batch shipment** holds many numbers as one migration: preflight the whole list, import the CSV, stage the carrier handoff, and drive the per-row LOA lifecycle deliberately. Each row in the batch is its own porting request (`status: submitted`) with its own state — the shipment is your bookkeeping, not a carrier concept.

Two batch-specific rules fall out of that:

* **Bulk import never auto-dispatches.** `POST /numbers/port-in/bulk-csv` stores one request per valid row and stops there — no carrier call fires inside the upload. The carrier handoff happens when your team attaches the LOA and country per row through the normal flow. That gap between "rows stored" and "rows dispatched" is the staging window this whole guide is organized around.
* **International rules re-verify per number, not per shipment.** US/CA numbers get a live synchronous portability check; numbers in DIDWW territory come back `check-not-supported` no matter how many times you ask, and eligibility is verified manually per number. A mixed-country batch splits into two verification regimes — plan the shipment so every DIDWW row's manual confirmation is done before any row dispatches, not discovered after the LOA is signed.

Shipment-scoped behavior in one sentence: the batch is a *staging and tracking* construct; authorization and eligibility stay per-number.

## 2. When each path applies

Pick the path before you prep the CSV:

| Situation                                   | Path                                                                 | Why                                                                                |
| ------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| One number, CSR in hand, signer ready       | Single create (`POST /numbers/porting`, full dispatch)               | Direct carrier order, the shortest line to FOC                                     |
| One number, LOA not signed yet              | Single create with `draft: true` (wizard checkpoint)                 | Stores the row without dispatching; the LOA lifecycle resumes it                   |
| 2–49 numbers, one account, one CSR          | Multi-number create (`numbers` array on `POST /numbers/porting`)     | One request, one LOA, one carrier order — the numbers travel together              |
| 50–1000 numbers, or many accounts/addresses | Bulk CSV (`POST /numbers/port-in/bulk-csv`)                          | One import, one request per row, staged LOA/c dispatch per row                     |
| The port will be driven by ops out-of-band  | Create with `country` omitted (manual mode)                          | No provider is ever selected; ops hand-processes the port                          |
| Porting numbers **off** Orbit in a group    | Port-out (`POST /numbers/porting/out`) per number, or bulk port-outs | Per-number PIN gate still applies; see [Port out safely](/guides/port-out-numbers) |

The 50-number line is where the single-request shape stops paying: the bulk CSV import caps at 1000 rows per request with a 2MB file budget, and above a single port's one-carrier-account assumption breaks down anyway — a retail chain's twelve regional lines often sit on three losing-carrier accounts, which forces one request per account regardless of size.

## 3. Bulk checklist — the shipment order

Run these in order. Each step protects the next: preflight gates the CSV, the CSV gates staging, staging gates the LOA wave.

1. **Build the E.164 list** — one number per line, `+` prefix, copied from the losing carrier's inventory rather than retyped.
2. **Pull the CSR per account** — a batch across N losing-carrier accounts needs N CSRs. Guessed fields are the #1 rejection class; `NAME_MISMATCH` and `ADDRESS_MISMATCH` trace to retyped CSR data, and in a batch they multiply.
3. **Preflight the whole list before importing anything** — the dashboard's **Pre-flight portability review** card on the [Number Porting](/numbers/porting) page runs `POST /numbers/porting/check/bulk` against the pasted list and renders a per-number verdict plus a summary rollup: *portable* (carrier-confirmed eligible), *ineligible* (carrier rejects, reasons listed), and *need manual check* (the carrier has no live eligibility API, or the check was skipped). Drop ineligible and unresolved-manual rows from the shipment; a non-portable DID discovered now costs nothing, discovered after the LOA costs the carrier's review window. The API equivalent returns the same `summary` block (`total / eligible / ineligible / not_supported / skipped`) for pipeline use.
4. **Lint the CSR fields with `pre-validate`** — the combined gate runs the portability check AND statically checks the CSR fields; gate on `ready: true`. In a batch this is where an account-number transposition across thirty rows surfaces before it submits.
5. **Build the CSV** — header row with exactly these columns (order irrelevant, case-insensitive):

```
phone_number, current_carrier_account_number, current_carrier_name, billing_name, billing_address
```

Quote any field containing a comma (`billing_address` almost always needs it). A header missing a required column aborts the whole upload with `422`. 1000 rows per request, 2MB total — split a larger estate across uploads rather than retrying a timed-out one.
6\. **Import the CSV** — `POST /numbers/port-in/bulk-csv` with the file as multipart. Each valid row becomes its own stored request; duplicate submissions against an active in-flight request are skipped and reported (`duplicates_skipped`, `overall_result: "partial"` when some rows failed). Read the `validation_errors` array per row.
7\. **Stage the carrier handoff** — the rows are stored, `status: submitted`, nothing dispatched. This is the deliberate gap: file the LOA per row (upload → sign → submit) on the schedule you actually control. A 12-number retail migration is exactly the case for staging — dispatch 3–4 numbers per wave so a CSR mismatch on wave one corrects the CSV before wave two ships, instead of rejecting twelve rows in parallel.
8\. **Drive the LOA wave per row** — the same three-step lifecycle per request: upload → sign → submit. In a batch, the discipline that matters is per-row: one LOA must list every number in that request (a multi-number create) or that one number (a CSV-imported row), signed within 30 days, legible at ≥200 dpi.

Do not park a staged row. A stored request that never gets its LOA never reaches the carrier, never gets an FOC, and ages silently — the dashboard flags exactly this state as "draft — LOA pending" for wizard drafts, and the discipline applies to CSV-staged rows the same way.

## 4. Track the shipment — webhooks first, polling as reconciliation

A 12-number shipment is twelve state machines. The pattern that keeps you out of a poll loop:

* **Webhooks drive the board.** Subscribe one endpoint to the porting events — `number.ported`, `porting.request.loa_signed`, `porting.request.supplement_submitted`, `porting.request.manual_review_required`, `porting.request.cancelled` — and let each transition move a row on your ops board. Delivery is at-least-once; dedupe on the event `id`.
* **A daily sweep reconciles.** `GET /numbers/porting` (list) plus `GET /numbers/porting/:id/timeline` per row is the reconciliation pass — anything a webhook delivery missed surfaces here. The timeline's per-stage view answers "where is row N stuck" without a carrier call.
* **Poll `refresh` only per-row, on demand.** `POST /numbers/porting/:id/refresh` re-polls the provider for one row; it is a targeted operator action, not a scheduling primitive. A cron that fans out `refresh` over a 500-row batch is a carrier call per page load — the events-plus-sweep pattern is the one that scales.

The dashboard renders the same timeline and rejection explanation per request, so the split that works in practice is: events feed your own tooling, the dashboard is the operator's live board, and the API list+timeline is the nightly reconciliation. All three read the same underlying requests — submit by API, watch in the dashboard.

## 5. Post-cutover checklist — per row, then per shipment

`number.ported` fires exactly once per completed port. Treat it as the start of the cutover work, not the end:

**Per row (the number itself):**

* **Re-verify provisioning on the number** — SMS on a ported DID behaves identically to a purchased one, but only provision what the shipment intends: if voice stayed with the losing carrier deliberately, the SMS side is a hosted-messaging LOA (`POST /numbers/hosted-messaging`), not a port. Confirm which regime each row is in before declaring the row done.
* **Re-verify what the carrier actually split** — a preflight `splittable: true` verdict means the carrier may port voice and leave SMS behind; check each `number.ported` row against that expectation, and text-enable via hosted messaging when the split left SMS behind intentionally.
* **Assign the number into your routing** — a completed port does not attach the number to a voice flow, agent, or campaign; that step is yours.

**Per shipment (the migration):**

* **Sweep the batch against the preflight list** — every number you shipped should be either `completed` or explicitly accounted for (`rejected` with a supplement in flight, or cancelled on purpose). A shipment is not closed while a row sits in an unexplained state.
* **International rows get a second pass** — DIDWW-territory numbers verified manually before dispatch deserve a manual confirmation post-cutover too: dial the number, send an SMS to it, and check inbound routing before removing the old carrier from the account.
* **Toll-free is not on this list** — toll-free numbers never travel the LNP flow; a RespOrg change via `POST /numbers/porting/toll-free/resporg` is a different lifecycle with its own Somos-side administration. Keep toll-free out of the CSV shipment and run it as its own parallel migration.
* **Close the losing-carrier account deliberately** — the port completing does not close anything on the losing side; that confirmation is your migration ledger's last line.

## Worked example: a retail chain's twelve regional lines

Putting the sections together on the example in the brief — a retailer porting 12 regional storefront numbers off three losing-carrier accounts:

1. **Build the list** — 12 E.164 lines, 11 US, 1 CAN; the CAN line goes on the manual-verification plan from step one (DIDWW territory, `check-not-supported` expected).
2. **Pull three CSRs** — one per losing-carrier account; each storefront's billing address is copied verbatim.
3. **Preflight the 12** — the dashboard's pre-flight review card flags 11 portable, 1 manual (the CAN line), 0 ineligible; the CAN line is manually confirmed with its carrier before the CSV ships.
4. **`pre-validate` per account** — three calls (one CSR each) gate to `ready: true`.
5. **CSV import** — 12 rows across three account-column values, uploaded once; all 12 rows stored as individual requests, staged.
6. **Staged dispatch** — wave 1 dispatches the four numbers on the cleanest account; wave 2 (day +2) the next account; wave 3 the CAN line after its manual confirmation lands. A CSR mismatch on wave 1 corrects the remaining waves' CSV before they dispatch.
7. **LOA per wave** — each wave's rows get upload → sign → submit in one operator session, with the signer reviewing exactly the numbers in that wave.
8. **Track on events** — the ops board moves per webhook; a nightly list+timeline sweep reconciles.
9. **Cutover per row** — as each `number.ported` fires, the storefront's routing attaches and the shipment checklist marks the row; the CAN line gets its dial-and-SMS double-check.
10. **Close** — once all 12 rows are `completed` (or explicitly accounted for), the ledger closes and the losing-account confirmation goes out.

The whole shape: preflight once, import once, stage deliberately, LOA in waves, track by events, cutover per row. The batch does not change the per-number state machine — it changes whose discipline keeps twelve of them from stalling in parallel.

## See Also

* [Port a number end-to-end](/guides/port-numbers) — the per-endpoint runbook this guide orchestrates.
* [Port out safely](/guides/port-out-numbers) — the symmetric runbook for batch port-outs, including the per-number PIN gate.
* [Your first port-in walkthrough](/guides/number-porting-walkthrough-first-port) — the shorter checklist for the single-number case.
* [Number Porting endpoint sequence](/numbers/porting) — the per-endpoint reference.
* [Webhook Events](/webhooks/events) — the full event catalog.
