> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Phone numbers

> Search carrier inventory, then purchase, list, suspend, reactivate, release, and re-apply voice configuration to the numbers an organization owns

The phone-number routes buy and manage the numbers an [organization](/concepts/credentials) owns: search carrier inventory, purchase a number, list and inspect the organization's numbers, suspend and reactivate one, release it, and re-apply a `vonage` number's carrier voice configuration. [`lua channels`](/reference/cli/channels) manages numbers interactively (search, purchase, bind to an agent, unbind, list, release) and has no non-interactive form; suspend, reactivate, and configure-voice have no CLI equivalent. The carrier compliance records a US number needs before it can send SMS, 10DLC brands and campaigns and toll-free registrations, are on [SMS registrations](/reference/rest/sms-registrations); the end-to-end setup is [SMS and phone numbers](/channels/sms-and-phone-numbers).

*Verified against lua-cli 3.33.0.*

## Base URL and authentication

Every route sits under `https://api.heylua.ai/developer` and takes `Authorization: Bearer <<YOUR_API_KEY>>`. Routes under `orgs/:orgId` check a `telephony` scope on the organization in the path: `telephony:read` lists and inspects, `telephony:write` suspends, reactivates, and configures voice, `telephony:manage` releases, and `telephony:provision` purchases. `telephony:provision` is a sensitive scope: a wildcard grant never covers it, so a scoped key must hold it by name. The inventory search needs only an authenticated key. `msisdn` path parameters are E.164 digits without the leading `+`, for example `12025550100`. A field that fails validation answers `400` with `message` as an array of the failed rules. Keys, scopes, and the error envelope are on the [REST API overview](/reference/rest/overview).

## Phone number object

<ResponseField name="id" type="string">Record ID.</ResponseField>
<ResponseField name="msisdn" type="string">The number in E.164 digits.</ResponseField>
<ResponseField name="country" type="string">ISO 3166-1 alpha-2 country code.</ResponseField>
<ResponseField name="type" type="string">One of `mobile-lvn`, `landline`, `landline-toll-free`.</ResponseField>
<ResponseField name="capabilities" type="string[]">Carrier features, for example `voice`, `sms`.</ResponseField>
<ResponseField name="orgId" type="string">Owning organization.</ResponseField>
<ResponseField name="purchasedBy" type="string">User ID of the purchaser.</ResponseField>
<ResponseField name="purchasedByEmail" type="string">Purchaser email, when known.</ResponseField>
<ResponseField name="status" type="string">One of `active`, `suspended`, `released`.</ResponseField>
<ResponseField name="createdAt" type="number">Unix time in milliseconds.</ResponseField>
<ResponseField name="transport" type="string">Carrier the number is provisioned through: `livekit`, `vonage`, or `byo-sip`.</ResponseField>
<ResponseField name="tenDlcCampaignId" type="string">ID of the linked [10DLC campaign](/reference/rest/sms-registrations#campaign-object), if any.</ResponseField>
<ResponseField name="tfnRegistrationId" type="string">ID of the attached [toll-free registration](/reference/rest/sms-registrations#registration-object), if any.</ResponseField>

## Endpoints

### GET /developer/phone-numbers/available

Searches carrier inventory for numbers you can buy. Any authenticated key; searches count against vendor-spend limits.

<ParamField query="country" type="string" required>ISO 3166-1 alpha-2 code, for example `US`. Upper-cased by the server.</ParamField>
<ParamField query="transport" type="string" default="livekit">Inventory to query: `livekit` or `vonage`. `byo-sip` numbers come from a [SIP trunk](/reference/rest/sip-trunks) and answer `400`.</ParamField>
<ParamField query="limit" type="number" default={10}>Maximum results, capped at 50; the server may configure a lower cap.</ParamField>
<ParamField query="type" type="string">`vonage` only: `mobile-lvn`, `landline`, or `landline-toll-free`.</ParamField>
<ParamField query="features" type="string">`vonage` only: comma-separated features, for example `voice,sms`.</ParamField>
<ParamField query="contains" type="string">`vonage` only: digit pattern to match in the number.</ParamField>
<ParamField query="search_pattern" type="number">`vonage` only: how `contains` matches, `0` starts with, `1` contains, `2` ends with.</ParamField>

**Response**

`200` with `{ numbers: [{ msisdn, country, type, features, cost?, transport }], count }`, where `cost` is present when the carrier reports a price and `count` is the length of `numbers`.

### POST /developer/orgs/:orgId/phone-numbers

Purchases a number for the organization. Scope `telephony:provision`.

<ParamField body="msisdn" type="string" required>The number to buy, from the availability search.</ParamField>
<ParamField body="transport" type="string" default="livekit">`livekit` or `vonage`; `byo-sip` answers `400`.</ParamField>
<ParamField body="country" type="string">Country code. Required when `transport` is `vonage`; derived from inventory otherwise.</ParamField>

**Response**

`201` with the [phone number object](#phone-number-object).

### GET /developer/orgs/:orgId/phone-numbers

Lists the organization's numbers. Scope `telephony:read`.

<ParamField query="status" type="string">`active`, `suspended`, or `released`.</ParamField>
<ParamField query="country" type="string">Filter by country code.</ParamField>
<ParamField query="transport" type="string">`livekit`, `vonage`, or `byo-sip`.</ParamField>
<ParamField query="limit" type="number" default={50}>Maximum results.</ParamField>
<ParamField query="offset" type="number">Results to skip.</ParamField>

**Response**

`200` with `{ numbers: PhoneNumber[], count }`, where `count` is the total matching the filters, not the page length.

### GET /developer/orgs/:orgId/phone-numbers/:msisdn

Returns one number. Scope `telephony:read`.

**Response**

`200` with the [phone number object](#phone-number-object). `404` `Phone number not found` when the organization does not own the number or has released it.

### DELETE /developer/orgs/:orgId/phone-numbers/:msisdn

Releases an `active` number back to the carrier; this cannot be undone. Scope `telephony:manage`. A `byo-sip` number answers `400`; a number that is not active answers `404` `Phone number not found or already released`.

**Response**

`200` with `{ "success": true }`.

### POST /developer/orgs/:orgId/phone-numbers/:msisdn/suspend

Suspends an `active` number: the organization keeps it, but calls are not routed. Scope `telephony:write`.

**Response**

`201` with the updated [phone number object](#phone-number-object); `404` `Phone number not found or not active` otherwise.

### POST /developer/orgs/:orgId/phone-numbers/:msisdn/reactivate

Reactivates a `suspended` number. Scope `telephony:write`.

**Response**

`201` with the updated [phone number object](#phone-number-object); `404` `Phone number not found or not suspended` otherwise.

### POST /developer/orgs/:orgId/phone-numbers/:msisdn/configure-voice

Re-applies the carrier voice configuration to an `active` `vonage` number, for when it failed during purchase. Scope `telephony:write`. `400` `Phone number does not have voice capability` on an SMS-only number; `404` `Phone number not found or not active` otherwise. A `livekit` number cannot be configured here and fails at the carrier.

**Response**

`201` with `{ "success": true }`.

## Errors

| Status | Message                                                                                    | Meaning                                                                                                             |
| ------ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
| `400`  | `Country is required`                                                                      | Availability search without `country`; pass it                                                                      |
| `400`  | `Unsupported transport: <value>`                                                           | A search with a transport other than `livekit` or `vonage`, or a purchase with `byo-sip`; use one of the two        |
| `400`  | `msisdn should not be empty`                                                               | Purchase body without `msisdn`; pass the number from the search                                                     |
| `400`  | `This number is already owned`                                                             | The number is in an organization; search again and pick another                                                     |
| `400`  | `country is required for vonage purchases`                                                 | `transport: "vonage"` without `country`; pass it                                                                    |
| `400`  | `Cannot release number with transport: byo-sip`                                            | Release of a bring-your-own SIP number; remove it from your SIP trunk instead                                       |
| `400`  | `Phone number does not have voice capability`                                              | `configure-voice` on an SMS-only number; buy a number with the `voice` feature                                      |
| `400`  | Validation messages                                                                        | A field failed a rule named in `message`; fix the field                                                             |
| `403`  | —                                                                                          | The key lacks the scope on this organization; `telephony:provision` must be granted by name                         |
| `404`  | `Phone number not found`, `… or already released`, `… or not active`, `… or not suspended` | The number is unknown to the organization or in the wrong status for the action; check `status` with the list route |

## Example

Purchase a number found with the availability search. The CLI does this only through the interactive `lua channels` menu, so there is no CLI tab.

<CodeGroup>
  ```ts TypeScript theme={null}
  const response = await fetch('https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/phone-numbers', {
    method: 'POST',
    headers: { Authorization: 'Bearer <<YOUR_API_KEY>>', 'Content-Type': 'application/json' },
    body: JSON.stringify({ msisdn: '12025550100' }),
  });
  if (!response.ok) throw new Error(`Purchase failed: ${response.status} ${await response.text()}`);
  const number: { id: string; msisdn: string; status: 'active' | 'suspended' | 'released'; transport: string } =
    await response.json();
  console.log(number.msisdn, number.status, number.transport);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/phone-numbers" \
    -H "Authorization: Bearer <<YOUR_API_KEY>>" \
    -H "Content-Type: application/json" \
    -d '{ "msisdn": "12025550100" }'
  ```
</CodeGroup>

## See also

* [SMS registrations](/reference/rest/sms-registrations) — 10DLC brands and campaigns, and toll-free registrations, for US SMS
* [SMS and phone numbers](/channels/sms-and-phone-numbers) — buy a number and bind it to an agent
* [Voice calls](/channels/voice-calls) — inbound calls on a purchased number
* [`lua channels`](/reference/cli/channels) — the interactive number menu
* [`lua voice`](/reference/cli/voice) — call a bound number from the terminal
