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

# Low DID inventory alerts and the auto-replenishment loop

> How the daily low-stock sweep warns you when a (country, area code) DID pool runs low, and how the replenishment policy previews and places replacement orders to hold the pool above its floor.

# Low DID inventory alerts and the auto-replenishment loop

A sender pool, a geo-matched dialer, a 10DLC rotator: each is a stock of DIDs under a (country, area code) corridor, and each is only as resilient as its available count. Orbit ships two complementary controls for that stock: a daily low-stock sweep that warns you when a pool runs low, and a replenishment policy that previews and places replacement orders to top the pool back up. This page explains how the two connect.

## The delivery cliff

Pools exist to spread outbound traffic across many DIDs so no single number carries enough volume to attract carrier throttling. When a pool's available stock silently exhausts (numbers scheduled for release, parked, or held in compliance), the same traffic concentrates on the survivors. Carriers rate spam and complaints per MSISDN, so the first visible symptom is a deliverability cliff on the one or two remaining senders, not an empty inventory list.

The cliff is silent because nothing in the send path fails. Messages keep going out, from fewer and fewer DIDs, until per-number rates trip. Catching it early means watching each pool's available count, which is exactly what the low-stock sweep does.

For how pools feed routing, see [Sender pool selection model](/concepts/sender-pool-selection-model), [Sender and routing](/concepts/sender-and-routing), and [Least-cost routing](/concepts/least-cost-routing).

## The daily low-stock sweep

Once a day at 04:30 UTC, Orbit reads your active numbers, groups them by (country code, area code prefix), and counts the available DIDs in each group. For NANP numbers the prefix is the 3-digit area code; other numbering plans use a derived prefix. A number counts as available when it is active, its release is not pending or failed, and it is not held at `pending_compliance`.

When a group's available count drops below your threshold, the sweep raises a low-stock alert for that group. The threshold is set per organization at `organizations.settings.numbers.tn_low_stock_threshold`, an integer from 1 to 1000. When the setting is absent or invalid, the platform default of 3 applies.

The sweep is strictly read-only. It counts inventory and raises alerts; it never modifies a number and never places an order. Restocking is a separate, explicit action, described below.

## The two alert surfaces

The sweep publishes the same alert on two surfaces:

* **Webhook.** One `number.inventory.low` event per depleted group, delivered to your active webhook endpoints. The payload carries `country_code`, `area_code_prefix`, `available_count`, `threshold`, and `raised_at`, so an integration can act per corridor without parsing a multi-bucket payload. See [Webhook events](/reference/webhook-events) for the event contract.
* **Dashboard banner.** The same computation backs `GET /api/v1/numbers/inventory-alerts`, the read endpoint the dashboard uses to render the low-stock banner.

## Wiring the alert to replenishment

An alert tells you a pool dropped; the replenishment endpoints decide what to do about it. They live under `/api/v1/numbers` and persist their policy at `organizations.settings.numbers.replenishment`.

### Preview first: `GET /numbers/replenishment-plan`

The plan endpoint computes, against your saved policy and your live available counts, what a replenishment run would order right now. It is read-only and safe to call from automation on every alert:

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

The response lists one item per pool below its floor, with `available`, `target_available`, `order_count`, and whether the order was capped by the per-run limit, plus a `summary` with `pools_to_replenish` and `total_to_order`.

### Declare the policy: `PUT /numbers/replenishment-policy`

A policy is a master `auto_order` switch plus a list of rules. Each rule names a pool and a floor:

```bash theme={null}
curl -X PUT https://api.orbit.devotel.io/api/v1/numbers/replenishment-policy \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "auto_order": false,
    "rules": [
      {
        "country": "US",
        "area_code": "415",
        "number_type": "local",
        "target_available": 3,
        "reorder_below": 2,
        "max_per_run": 5
      }
    ]
  }'
```

* `country` is an ISO alpha-2 code, normalized to upper case. `area_code` scopes the rule to one corridor; omit it to pool every area code under the country. `number_type` is `local`, `mobile`, or `toll_free` (default `local`).
* `reorder_below` is the floor: a run orders for the rule when available stock is below it. `target_available` is the level the order refills up to, and must be at least `reorder_below`.
* `max_per_run` caps one run's orders for the rule. `GET /numbers/replenishment-policy` returns your saved policy together with the platform defaults and ceilings, so you can validate values before saving. An invalid policy returns `422` with field-level details.
* `auto_order` (default `false`) is the master switch for unattended ordering. With it false you can still preview the plan and execute an explicit run; no background actor orders on your behalf.

### Execute: `POST /numbers/replenishment/run`

A run computes the same plan and executes it, ordering each pool's replacement DIDs through the same reservation and purchase lifecycle as a manual bulk purchase, including the wallet check. Each rule is bounded by its per-run cap; if the wallet cannot cover an order, the run releases the hold, halts, and reports `halted: true`.

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

The response reports, per pool, how many numbers were ordered and succeeded, with a `status` of `ordered`, `partial`, `no_inventory`, `order_failed`, or `halted`.

A recommended reaction flow for webhook-driven automation: on `number.inventory.low`, either call `GET /numbers/replenishment-plan` to preview what would order now and page a human, or submit `POST /numbers/replenishment/run` directly when the saved policy is itself your approval.

## The loop end to end

```mermaid theme={null}
sequenceDiagram
  participant Sweep as Low-stock sweep, 04:30 UTC
  participant Hook as Your webhook endpoint
  participant Auto as Your automation
  participant API as Numbers API
  Sweep->>Hook: number.inventory.low (US / 415, available_count 1)
  Hook->>Auto: event delivered
  Auto->>API: PUT /numbers/replenishment-policy for US / 415
  Auto->>API: GET /numbers/replenishment-plan
  API-->>Auto: one plan item, order_count 2
  Auto->>API: POST /numbers/replenishment/run
  API-->>Auto: status ordered, 2 succeeded
```

## Worked example: a 3-DID pool comes back

1. A geo-matched pool holds three active DIDs in (US, 415). Two leave the available count: one scheduled for release, one held at `pending_compliance`. Available count: 1.
2. The next 04:30 UTC sweep groups the pool as (US, 415), counts 1 available against the default threshold of 3, and fires `number.inventory.low` with `available_count: 1`.
3. Your automation receives the event and saves a policy rule for the corridor: `target_available: 3`, `reorder_below: 2`, `max_per_run: 5`.
4. It calls `GET /numbers/replenishment-plan`. The plan shows one item: available 1, target 3, `order_count` 2.
5. Your approval step passes and the automation calls `POST /numbers/replenishment/run`. The result reports `status: "ordered"`, 2 succeeded.
6. The two replacement DIDs land active in the pool. The next sweep counts 3 available, above both the alert threshold and the reorder floor, so no alert fires.

## Boundaries

* The sweep is read-only. It never releases, suspends, or purchases a number; its only side effect is publishing alerts.
* Replenishment never orders on ambient traffic. Orders happen only inside `POST /numbers/replenishment/run`, or an unattended actor you explicitly enable with `auto_order: true`, always against a saved policy.
* A failed availability read aborts a plan or run before any order is placed, rather than guessing at zero stock.
* Replacement DIDs follow the standard purchase path, wallet and audit included. See [Number bulk reservation model](/concepts/number-bulk-reservation-model).

## See also

* [Sender pool selection model](/concepts/sender-pool-selection-model)
* [Sender and routing](/concepts/sender-and-routing)
* [Least-cost routing](/concepts/least-cost-routing)
* [BYO carrier lifecycle](/concepts/byo-carrier-lifecycle)
* [Export number inventory](/numbers/inventory-export)
* [Number status map](/concepts/number-lifecycle)
* [Webhook events](/reference/webhook-events)


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