# Buy a number

> Buy a number and make it an SMS channel in the same call. The purchase follows the same rules as POST /v1/phone-numbers, including regulatory requirements, and the number comes back with SMS on. There is no separate assign step. Where it lands: when the project's default channel group has no number yet, the number joins that group (and becomes the project's first channel and default when there is none); otherwise it gets a channel group of its own.

`POST https://api.zavu.dev/v2/numbers`

Buy a number and make it an SMS channel in the same call. The purchase follows the same rules as `POST /v1/phone-numbers`, including regulatory requirements, and the number comes back with SMS on. There is no separate assign step. Where it lands: when the project's default channel group has no number yet, the number joins that group (and becomes the project's first channel and default when there is none); otherwise it gets a channel group of its own.

While the number is in regulatory review (`number.regulatoryStatus: pending_review`) it is attached but cannot send, and `channels` can be empty until it is approved. If the purchase succeeded but turning SMS on failed, the number is still bought and billed; the response says so in `setupError` rather than failing the call.

## Body

- `phoneNumber` (string, required): Phone number in E.164 format.
- `name` (string): Optional custom name for the phone number.
- `type` (string): Type the number was found under in `GET /v2/numbers/available`. Used to look the number up and to keep regulatory information for reuse. Required when `regulatoryRequirements` is sent. `toll_free` is accepted as an alias of `tollFree`. A number not listed under this type is refused with `400 number_unavailable` saying so. One of: `local`, `national`, `tollFree`, `mobile`.
- `regulatoryRequirements` (object[]): Regulatory information, for numbers whose requirements list is not empty. Get the list with `GET /v1/phone-numbers/requirements?phoneNumber=...` and send one entry per requirement id, except `action` requirements, which take no value. Every required id must be present, once, and no unknown id may be sent; otherwise the purchase is refused with `400 invalid_request` before anything is charged. The information is kept for your project under the number's country and `type`. A later purchase there may omit this field if what is kept still covers that number's requirements. Omit it for numbers without requirements.
  - `requirementType` (string, required): A `requirementTypes[].id` from `GET /v1/phone-numbers/requirements`. Each id may appear only once.
  - `fieldValue` (string, required): Depends on the requirement's `type`: the text itself for `textual`; for `address`, the `id` of an address created in this project with `POST /v1/addresses`; for `document`, the `id` of a document created with `POST /v1/documents`. An address or document from another project, or one rejected in review, is refused.

## Responses

### 201

Bought.

- `number` (object, required)
  - `id` (string, required)
  - `phoneNumber` (string, required)
  - `name` (string): Optional custom name for the phone number.
  - `capabilities` (string[], required)
  - `status` (string, required): Billing state of an owned number, separate from `regulatoryStatus`. `pending` is legacy and is not written to numbers today. One of: `active`, `suspended`, `pending`, `releasing`, `released`.
  - `pricing` (object, required)
    - `monthlyPrice` (number): Monthly price in USD.
    - `upfrontCost` (number): One-time purchase cost in cents.
    - `monthlyCost` (number): Monthly cost in cents.
    - `isFreeNumber` (boolean): Whether this is a free number.
  - `regulatoryStatus` (string, required): Regulatory review state. Numbers that need no review are `approved` immediately. A number bought with regulatory information is owned and billed from purchase and starts `pending_review`; it cannot send messages or place calls until this is `approved`, and its `channels` entry can be absent until then. The state is re-checked every 6 hours. One of: `approved`, `pending_review`, `rejected`.
  - `nextRenewalDate` (string<date-time>)
  - `createdAt` (string<date-time>, required)
  - `updatedAt` (string<date-time>)
- `channels` (object[], required): The entries of the new number (`sms`, once routed).
  - `address` (string, required): The address the recipient sees: an E.164 number (`sms`, `voice`, `whatsapp`), an email address (lowercase), `@botname` (`telegram`), `@username` (`instagram`), the Facebook Page id (`messenger`), or the literal `sms_oneway` (one-way SMS goes out from shared system numbers, and messages on it store this same literal as `from`).
  - `type` (string, required): A transport a message travels on. One of: `sms`, `sms_oneway`, `whatsapp`, `telegram`, `email`, `instagram`, `messenger`, `voice`.
  - `active` (boolean, required): It can send right now. A connected transport that is switched off is listed with `active: false`; turn it on with `POST /v2/channels/{address}/{type}/activate`. `sms`, `sms_oneway` and `voice` have no switch and are always active.
  - `v1SenderId` (string, required): Read-only. The /v1 sender behind this entry, usable as the `Zavu-Sender` header on /v1. Entries with the same `v1SenderId` are one channel group. On /v2 it is accepted as `from` only when two entries of different groups share an address (see `409 ambiguous_from`).
- `setupError` (object): Present only when the purchase succeeded and turning SMS on did not. `details.recommendation` says how to retry.
  - `code` (string, required)
  - `message` (string, required)
  - `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
    - `errors` (object[])
      - `path` (object[], required)
      - `message` (string, required)
      - `code` (string)

### 400

`number_unavailable`, `regulatory_compliance_required`, `invalid_request`.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 402

`insufficient_balance` or `paid_plan_required`.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 502

`requirements_unavailable`; nothing charged.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

## Example

```bash
curl -X POST "https://api.zavu.dev/v2/numbers" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phoneNumber": "+15551234567"
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.v2.numbers.purchase({
  phoneNumber: "+15551234567",
});

console.log(result);
```
