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

# Number backorder waitlist: get notified when an exhausted area code restocks

> Subscribe to a standing backorder for a country, area code, and number type that is out of stock at the provider, poll live availability until the pool restocks, then place the purchase through the normal buy flow.

# Number backorder waitlist

When the number you want is out of stock at the provider, the synchronous buy
flow cannot help — there is nothing to search, select, or purchase. The
backorder waitlist lets you register a standing request for a specific
`(country, area code, number type)` pool and poll the same live provider
inventory the buy flow uses, so the moment matching stock reappears you can
place the order instead of manually re-running the search page.

This guide covers what the waitlist is, the entry lifecycle, the dashboard
walkthrough, an automation pattern, the limits and behaviour, and when to
reach for the waitlist instead of the replenishment policy or a low-stock
alert.

## What the waitlist is

A waitlist entry is a standing backorder for a pool of numbers that is
**currently exhausted at the provider** — `GET /api/v1/numbers/available`
returns no rows for the `(country, area code, number type)` combination you
want. You tell Orbit what you are waiting for and how many you want, and the
availability check re-runs the live, cross-provider inventory search
(`Telnyx` + `DIDWW` + `Devotel`) on demand until the pool reports enough depth
to satisfy your request.

The waitlist is distinct from two supply-side surfaces that both **presume
availability** and therefore cannot help when a pool is empty:

| Surface | What it operates over | When the pool is exhausted |
| - | - | - |
| **Buy flow** (`POST /numbers/purchase`, `/buy-bulk`) | Inventory present right now — a row returned by `GET /numbers/available` | No row to select; nothing to buy |
| **Replenishment policy** (`POST /replenishment/run`) | Inventory you already own — auto-buys more from orderable stock when your owned pool runs low | The desired area code has no orderable stock to draw from |
| **Low-stock alert** (`GET /numbers/inventory-alerts`) | Your owned inventory — warns when a bucket you hold drops below a threshold | The bucket you want does not exist in your inventory at all |
| **Backorder waitlist** (`POST /numbers/waitlist`) | A provider pool that currently returns no inventory | Registers the request and re-checks live availability until stock reappears |

The buy flow answers "the number is in stock — take it now". Replenishment
answers "I own a pool and it is running low — top it up from orderable stock".
The low-stock alert answers "warn me before my owned bucket empties". The
waitlist answers "the number is out of stock — tell me when it comes back".

## The lifecycle

The waitlist is a four-endpoint surface. All routes are tenant-scoped and
require the `numbers:write` scope (reads work with `numbers:read`).

| Method | Path | Purpose |
| - | - | - |
| `GET` | `/api/v1/numbers/waitlist` | List your standing entries |
| `POST` | `/api/v1/numbers/waitlist` | Subscribe — place a backorder |
| `DELETE` | `/api/v1/numbers/waitlist/:entryId` | Unsubscribe — remove an entry |
| `GET` | `/api/v1/numbers/waitlist/availability` | Poll live provider inventory for every enabled entry |

### 1. Create an entry

Subscribe to a backorder with `POST /api/v1/numbers/waitlist`:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/waitlist \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "US",
    "area_code": "415",
    "number_type": "local",
    "quantity": 10,
    "note": "Support line expansion — SF market"
  }'
```

| Field | Type | Required | Description |
| - | - | - | - |
| `country` | string | yes | ISO 3166-1 alpha-2 code (e.g. `US`). Case-insensitive on input; upper-cased on store. |
| `area_code` | string | no | 1–6 digit area/numbering prefix. For NANP this is the 3-digit area code. Omit to wait on **any** area code under the country. |
| `number_type` | string | no | One of `local`, `mobile`, `toll_free`. Defaults to `local`. |
| `quantity` | integer | no | How many matching numbers you want once stock returns (1–1000). Defaults to `1`. Used to decide whether a restocked pool is deep enough to satisfy the request. |
| `note` | string | no | Free-text reminder (max 280 chars). |

The server assigns `id`, `created_at`, and `created_by` and returns the stored
entry:

```json theme={null}
{
  "data": {
    "entry": {
      "id": "numberWaitlistEntry_01JAE...",
      "country": "US",
      "area_code": "415",
      "number_type": "local",
      "quantity": 10,
      "note": "Support line expansion — SF market",
      "enabled": true,
      "created_at": "2026-10-11T12:00:00.000Z",
      "created_by": "user_abc123"
    }
  }
}
```

An exact duplicate — the same `(country, area_code, number_type)` already on
your waitlist — returns `409 WAITLIST_DUPLICATE` with the `existing_entry_id`,
so the waitlist cannot fill with repeated subscriptions to the same pool. The
hard ceiling is **50 entries per organization**; exceeding it returns
`422 WAITLIST_LIMIT_REACHED`.

### 2. Poll availability

`GET /api/v1/numbers/waitlist/availability` runs a live provider inventory
search for every enabled entry and reports whether each pool has restocked
enough to satisfy the request:

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

```json theme={null}
{
  "data": {
    "summary": {
      "enabled": 3,
      "checked": 3,
      "truncated": false,
      "fulfillable": 1
    },
    "results": [
      {
        "entry_id": "numberWaitlistEntry_01JAE...",
        "country": "US",
        "area_code": "415",
        "number_type": "local",
        "requested_quantity": 10,
        "available_count": 14,
        "fulfillable": true,
        "providers": [
          { "provider": "devotel", "status": "ok", "count": 14 }
        ],
        "checked": true,
        "error": null
      }
    ]
  }
}
```

| Field | Type | Description |
| - | - | - |
| `entry_id` | string | The waitlist entry id. |
| `available_count` | integer | Provider-reported available depth across the union of providers. |
| `fulfillable` | boolean | `true` when `available_count >= entry.quantity` — the pool is deep enough to satisfy the request. |
| `providers` | array | Per-provider status and count (`devotel`, `telnyx`, `didww`). |
| `checked` | boolean | `false` when the live search threw for that entry (see `error`). |
| `error` | string\|null | Provider error message when the check failed for that entry; the sweep continues and reports the rest. |

An entry is **fulfillable** when the live available count is at least the
entry's `quantity`. The availability check fans out to up to **15** enabled
entries per request (one cross-provider search each, concurrency bounded to
3\). When your enabled set exceeds that cap, `summary.truncated` is `true` and
the response flags it — narrow the check with `?entry_id=<id>` to poll a
specific entry.

To check a single entry, pass its id:

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

### 3. Place the purchase

The waitlist never buys on your behalf. When an entry returns `fulfillable:
true`, place the order through the normal buy flow — the inventory is now
present:

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/buy-bulk \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "phone_number": "+14155550100", "country_code": "US" },
      { "phone_number": "+14155550101", "country_code": "US" }
    ]
  }'
```

Use `GET /numbers/available?country=US&type=local` with the entry's filters to
select the concrete E.164 numbers, then pass them to `POST /numbers/buy-bulk`
(up to 50 per call) or `POST /numbers/purchase` one at a time. The buy flow
wallet preflight, per-number mutex, and per-line error handling all apply
unchanged — the waitlist only tells you stock exists; it does not reserve it.
Stock can turn over between your availability poll and your purchase, so treat
`fulfillable: true` as "re-check and buy now", not as a hold.

### 4. Unsubscribe

Remove an entry with `DELETE /api/v1/numbers/waitlist/:entryId`:

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

Returns `200` with `{ removed: true, entry_id }`. An unknown id returns
`404 WAITLIST_ENTRY_NOT_FOUND`.

### Persistence

The waitlist lives on `organizations.settings.numbers.waitlist` as JSONB —
the same per-organization store the replenishment policy and low-stock
threshold already use, so no migration is required. Adding or removing an
entry is a read-modify-write of that blob, org-scoped by tenant id. A
malformed entry in the blob is dropped individually rather than discarding
the whole list, and any lookup or parse failure yields the empty waitlist.

## Dashboard walkthrough

Standing entries appear on the Numbers page under a **Waitlist** tab. Each
row shows the country, area code, number type, requested quantity, and the
last availability result. The **Recheck availability** action on a row runs
the same live provider search as `GET /numbers/waitlist/availability` for
that entry interactively, so you can poll a single pool without waiting on a
scheduled sweep.

When an entry returns `fulfillable: true`, the row surfaces a **Buy now**
action that drops you into the buy flow pre-filtered to that entry's
`(country, area code, number type)` — the same search you would run
manually, with the filters filled in. The waitlist does not hold the stock;
the action re-searches live inventory at click time, so act on a match
promptly.

## Automation pattern

A typical integration subscribes a backorder entry, polls availability on a
schedule, and fires a purchase when an entry returns `fulfillable: true`.
The waitlist does not push a notification — it is a poll-driven surface — so
the integration supplies the cadence.

### 1. Subscribe once

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/waitlist \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "country": "US",
    "area_code": "415",
    "number_type": "local",
    "quantity": 10,
    "note": "Auto-provisioned by stock monitor"
  }'
```

### 2. Poll on a schedule

Run `GET /numbers/waitlist/availability` on a cadence appropriate to how
quickly stock turns in the target market. Each entry costs one
cross-provider live search, and the sweep is bounded to 15 entries per call,
so a 15–30 minute interval is a reasonable default for a handful of entries:

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

When `summary.truncated` is `true`, your enabled set exceeds the per-request
cap — poll the remaining entries with `?entry_id=` in subsequent calls, or
disable entries you no longer need.

### 3. Buy when an entry is fulfillable

For every result where `fulfillable` is `true`, re-search the concrete
inventory and place the purchase:

```bash theme={null}
# Re-search the concrete rows for this entry's pool
curl "https://api.orbit.devotel.io/api/v1/numbers/available?country=US&type=local" \
  -H "X-API-Key: dv_live_sk_..."

# Buy up to 50 in one call
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/buy-bulk \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "phone_number": "+14155550100", "country_code": "US" },
      { "phone_number": "+14155550101", "country_code": "US" }
    ]
  }'
```

Handle `NUMBER_NO_LONGER_AVAILABLE` per-line failures the same way the buy
flow does — failed rows are never charged, so re-search the remaining
quantity and retry only the replacements. After the purchase lands, remove
the entry with `DELETE /numbers/waitlist/:entryId` if it is fully satisfied,
or leave it in place if you want to keep waiting for more depth.

A minimal Node.js loop:

```typescript theme={null}
import { Orbit } from "@devotel/sdk-node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

// Once: subscribe the backorder
await orbit.request("POST", "/numbers/waitlist", {
  country: "US",
  area_code: "415",
  number_type: "local",
  quantity: 10,
});

// On a schedule: poll, then buy every fulfillable entry
const { data } = await orbit.request("GET", "/numbers/waitlist/availability", undefined);
for (const r of data.results) {
  if (!r.fulfillable) continue;
  const found = await orbit.request("GET", "/numbers/available", {
    country: r.country, type: r.number_type,
  });
  const items = found.data.numbers.slice(0, r.requested_quantity)
    .map((n) => ({ phone_number: n.phone_number, country_code: r.country }));
  await orbit.request("POST", "/numbers/buy-bulk", { items });
  await orbit.request("DELETE", `/numbers/waitlist/${r.entry_id}`, undefined);
}
```

## Limits and behaviour

| Behaviour | Value | Where it comes from |
| - | - | - |
| Max entries per organization | 50 | `MAX_WAITLIST_ENTRIES` |
| Max quantity per entry | 1000 | `MAX_WAITLIST_QUANTITY` |
| Max note length | 280 characters | `MAX_WAITLIST_NOTE_LEN` |
| Availability checks per request | 15 entries (one cross-provider search each, concurrency 3) | `MAX_WAITLIST_AVAILABILITY_CHECKS`, `WAITLIST_AVAILABILITY_CONCURRENCY` |
| Availability search page | 20 rows per provider | `WAITLIST_SEARCH_LIMIT` |
| Number types | `local`, `mobile`, `toll_free` | `number_type` enum |
| Duplicate target | `409 WAITLIST_DUPLICATE` — same `(country, area_code, number_type)` already on the waitlist | `waitlistEntryKey` |
| Limit reached | `422 WAITLIST_LIMIT_REACHED` — 50 entries already | `MAX_WAITLIST_ENTRIES` |
| Unknown entry on delete | `404 WAITLIST_ENTRY_NOT_FOUND` | unsubscribe path |
| Truncated availability | `summary.truncated: true` when enabled entries exceed the per-request cap; narrow with `?entry_id=` | availability sweep |
| Fail-safe parse | A malformed entry in the blob is dropped individually; a lookup/parse failure yields the empty waitlist | `parseNumberWaitlist` |

The waitlist is **poll-driven**: there is no pushed notification when stock
reappears. The `number.inventory.low` webhook event covers your owned
low-stock buckets; it does not fire for a backorder entry. Your integration
supplies the poll cadence via `GET /numbers/waitlist/availability`.

The availability check is a **pure provider inventory read** — it runs the
same cross-provider search the buy flow uses and reports depth. It does not
reserve, hold, or purchase anything, and it never touches an outbound
voice/SMS path. Stock can turn over between a poll and a purchase, so a
`fulfillable: true` result means "re-check and buy now", not a held
reservation.

## When to use the waitlist vs replenishment vs a low-stock alert

The three supply-side surfaces answer different questions. Pick by the state
of the pool you care about:

| You want to… | The pool is… | Use |
| - | - | - |
| Buy a number right now | In stock at the provider | **Buy flow** — `POST /numbers/purchase` or `/buy-bulk` |
| Be notified when an exhausted area code restocks | Out of stock — the provider returns no inventory for the `(country, area code, number type)` you want | **Waitlist** — `POST /numbers/waitlist` + poll `/waitlist/availability` |
| Top up a pool you already own before it empties | Orderable at the provider, but your owned bucket is running low | **Replenishment policy** — `POST /replenishment/run` auto-buys from orderable stock |
| Get warned before an owned bucket empties | Owned and approaching a threshold | **Low-stock alert** — `GET /numbers/inventory-alerts` (and the `number.inventory.low` webhook) |

A useful shorthand: the **buy flow** is for stock that is present, the
**replenishment policy** is for stock you own that is running low, the
**low-stock alert** warns you before an owned bucket empties, and the
**waitlist** is for stock that does not exist yet. If `GET
/numbers/available` returns rows for your target, you do not need the
waitlist — buy directly. Reach for the waitlist only when the search is
empty for the pool you want.

## See Also

* [Phone Numbers](/numbers/overview) — the Numbers pillar surface: search, purchase, routing, porting.
* [Number Lifecycle](/numbers/lifecycle) — auto-renew, scheduled release, reassign, reclaim, and the trial pool.
* [Trial & shared-pool number model](/concepts/number-trial-pool-model) — the one free trial claim and the 24-hour lease.
* [Inventory & Export](/numbers/inventory-export) — list and export what you own, plus the low-stock alert surface.
* [Buy and provision numbers](/guides/buy-numbers) — the single-purchase and bulk-order walkthrough the waitlist hands off to.
* [Numbers API reference](/api-reference/numbers) — the full endpoint surface.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.