Skip to main content

Inventory & Export

Every number you own lives in your tenant inventory. List it page-by-page, export the whole set for finance reconciliation, and monitor low-stock buckets so you never run dry mid-campaign. All routes are tenant-scoped — you only ever see your own organization’s numbers.

List Numbers

The list endpoint is cursor-paginated (25 rows per page by default). For anything over ~100 numbers, use the bulk export below instead of paging.

Export Inventory

Download your entire number inventory in a single request — ideal for reconciling the monthly DID line on your invoice against the active set, or syncing your CMDB.

Query parameters

Response headers

CSV columns

Columns are emitted in this exact order (load-bearing for downstream billing tooling — new columns are only ever appended):
tfv_status is the Toll-Free Verification status of the number. It only applies to toll-free DIDs (not_submitted, pending, approved, rejected); for every other number type the column reads N/A because toll-free verification does not apply. spend_30d is the trailing-30-day total spend for the number — recurring monthly charge plus realised usage (voice + SMS + MMS) over the last 30 days — matching the Spend (30d) column on the dashboard Numbers list. A number with no traffic shows just its recurring cost; the cell is left blank only if the spend figure could not be computed. created_at, assigned_at, and next_billing_at are written in UTC ISO-8601 with a trailing Z and second precision — for example 2026-09-01T08:46:36Z. This is the format every spreadsheet (Excel, Google Sheets, Numbers) and every standard date parser reads as the same UTC instant, regardless of your time zone. An empty date (the number was never assigned, has no next billing date, etc.) is an empty cell. The on-screen Numbers table still renders these dates in your timezone; only the file uses the UTC form, so two operators in different zones always get byte-identical files for the same inventory.

JSON example

Export is a pure read of your local inventory table — no carrier call is made, and it never touches an outbound voice/SMS path. Any dashboard user with read access can export their own organization’s inventory.

Export from the dashboard

The same export is one click away in the dashboard — no API key needed.
  1. Open Numbers in the dashboard.
  2. Click Export inventory (CSV) in the page header (next to Buy Number). A spinner shows while the file generates, then the browser downloads phone-numbers-export-YYYY-MM-DD.csv and a toast confirms the download.
What the button sends for you:
  • Dates are always UTC in the file. The created_at / assigned_at / next_billing_at columns are written in UTC ISO-8601 with a trailing Z (e.g. 2026-09-01T08:46:36Z), so the file is identical for every operator regardless of their time zone and every spreadsheet or date parser reads the value as the same instant. The on-screen Numbers table still renders in your timezone; only the file uses UTC. The button still sends your browser’s timezone and locale for backward compatibility, but they no longer change the file’s date values.
  • Full inventory, not the visible slice. The export always covers the whole inventory — it does not inherit status-tab, country, or search filters you set on the list. To narrow a dashboard export, export everything and filter the CSV, or call the API with ?status=…&country_code=…&search=….
  • 50,000-row cap. Exports stop at 50,000 rows. When the cap hits, the API sets X-Truncated: true and X-Truncated-At: 50000 on the response — the CSV you receive is sorted and complete up to the cap. Narrow with the query parameters above to get under it.
The button always downloads the CSV format. If you need the JSON projection or a pre-filtered set, call GET /numbers/export directly with the parameters above.

Low-Stock Inventory Alerts

When you provision numbers from a pool (rotation, sticky-sender, campaign overflow), Orbit tracks how many active DIDs remain per (country, area-code prefix) bucket and flags any bucket below your threshold.

Query parameters

Response

The threshold defaults to 3 and can be overridden per-tenant via organizations.settings.numbers.tn_low_stock_threshold. A daily scheduler emits the low-stock bucket payload through the number.inventory.low webhook event so your integration can auto-reprovision without polling.
The API response carries no severity field. Severity levels exist only in the dashboard banner (below), which derives them from available_count relative to threshold. If you build your own alert surface, replicate that derivation — the rule is documented in the banner section.

Low-inventory banner (dashboard)

The dashboard consumes this endpoint for you and shows the result as a banner at the top of the Numbers page — the same data, no polling script required.
  • Hidden at steady state. When no bucket is below the threshold (total_alerts: 0) the banner renders nothing — absence means healthy stock, not an error. It also stays hidden while the alerts query is in flight, so it never flashes on page load.
  • One row per bucket. Each row names a (country, area_code_prefix) bucket and shows its current available_count against your configured threshold — for example US · 415 — 2 available (threshold 3). When more buckets are low than the banner displays you get a + N more buckets line, mirroring total_alerts - alerts.length from the API.
  • Two severity tiers. The whole banner takes the colour of its worst row:
    • Critical (red) — the bucket’s available_count is at or below half the threshold (available_count <= threshold / 2). At the default threshold of 3, a bucket with 0 or 1 available numbers is critical.
    • Warning (amber) — the bucket is below the threshold but above the half-threshold line.
    Severity level is also carried by an icon and an accessible label, never by colour alone. The dashboard computes severity client-side from available_count and threshold; the API intentionally ships the raw counts only, so your own integration can apply the same rule (or your own).
  • Buy more from the banner. The banner’s Buy numbers action takes you straight to the buy flow for replenishment. The purchase draws from your existing wallet balance — you are only asked to top up when the balance cannot cover the order.

Reads degrade, never 503

GET /numbers/inventory-alerts is an idempotent read with a deliberate failure policy: when the inventory store is briefly unavailable — a tenant mid-provisioning, a transient database blip, or a statement timeout — the endpoint returns the same empty envelope it returns when every bucket is healthy (alerts: [], total_alerts: 0) instead of a 503. The banner hides on that response and comes back on the next successful poll, so a blip reads as “no low-stock alerts” for a moment rather than as an error page. Design your own consumer the same way: treat an empty alerts array as “nothing actionable right now”, never as proof of healthy stock after a failed request.