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

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

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.



## OpenAPI

````yaml /api-reference/v2/openapi.json post /v2/numbers
openapi: 3.0.3
info:
  title: Zavu API v2
  version: 2.0.0
  description: >-
    The second version of the Zavu API. It runs on the same engine as /v1: the
    same API keys, the same per-project rate limit, the same sending limits,
    prices and reviews. /v1 keeps working unchanged, and you can mix calls to
    both.


    **Addresses, not senders.** /v2 has no sender object. Where a message leaves
    from is named the way the recipient sees it: an E.164 number, an email
    address, `@botname` for Telegram, `@username` for Instagram, the Facebook
    Page id for Messenger, and the literal `sms_oneway` for one-way SMS (sent
    from shared numbers, never a number of your own). `GET /v2/channels` lists
    one entry per (address, type); a number that carries WhatsApp and SMS is two
    entries with the same address.


    **What else changes.** No `Zavu-Sender` header (`from` or `conversationId`
    instead); every send reports how it was routed; one project webhook carries
    every event; every error is `{code, message, details}` at the root.


    **Wrong method.** A path under `/v2/messages`, `/v2/conversations`,
    `/v2/contacts`, `/v2/channels`, `/v2/templates`, `/v2/campaigns`,
    `/v2/agents`, `/v2/webhook` or `/v2/email-domains` called with a method it
    does not take answers `405 method_not_allowed`, with `details.allowed` and
    an `Allow` header.


    **Money.** Every amount is USD, rounded to 6 decimals.
servers:
  - url: https://api.zavu.dev
security:
  - bearerAuth: []
tags:
  - name: Messages
    description: Send, list and read messages.
  - name: Conversations
    description: Inbox threads, one per contact across channels.
  - name: Contacts
    description: Contacts and the identifiers they are reached at.
  - name: Channels
    description: 'Where the project can send from: one entry per (address, type).'
  - name: Templates
    description: WhatsApp message templates.
  - name: Campaigns
    description: One message to many recipients.
  - name: Webhook
    description: The one project webhook for every event.
  - name: Agents
    description: AI agents and the addresses they answer on.
  - name: Numbers
    description: Search and buy phone numbers.
  - name: Email Domains
    description: Domains to send email from.
  - name: Functions
    description: Configure Zavu Functions.
paths:
  /v2/numbers:
    post:
      tags:
        - Numbers
      summary: Buy a number
      description: >-
        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.
      operationId: purchaseNumberV2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PhoneNumberPurchaseRequest'
      responses:
        '201':
          description: Bought.
          content:
            application/json:
              schema:
                type: object
                required:
                  - number
                  - channels
                properties:
                  number:
                    $ref: '#/components/schemas/OwnedPhoneNumber'
                  channels:
                    type: array
                    description: The entries of the new number (`sms`, once routed).
                    items:
                      $ref: '#/components/schemas/ChannelEntry'
                  setupError:
                    allOf:
                      - $ref: '#/components/schemas/Error'
                    description: >-
                      Present only when the purchase succeeded and turning SMS
                      on did not. `details.recommendation` says how to retry.
        '400':
          description: >-
            `number_unavailable`, `regulatory_compliance_required`,
            `invalid_request`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: '`insufficient_balance` or `paid_plan_required`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: '`requirements_unavailable`; nothing charged.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  schemas:
    PhoneNumberPurchaseRequest:
      type: object
      required:
        - phoneNumber
      properties:
        phoneNumber:
          type: string
          description: Phone number in E.164 format.
          example: '+15551234567'
        name:
          type: string
          maxLength: 100
          description: Optional custom name for the phone number.
        type:
          allOf:
            - $ref: '#/components/schemas/PhoneNumberType'
          description: >-
            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.
        regulatoryRequirements:
          type: array
          maxItems: 50
          description: >-
            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.
          items:
            type: object
            required:
              - requirementType
              - fieldValue
            properties:
              requirementType:
                type: string
                description: >-
                  A `requirementTypes[].id` from `GET
                  /v1/phone-numbers/requirements`. Each id may appear only once.
                example: 8c5b1a2e-0f3d-4f5b-9a61-2c7e4d9b1f10
                minLength: 1
                maxLength: 200
              fieldValue:
                type: string
                maxLength: 1000
                description: >-
                  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.
                example: jd7x2k3m4n5p6q7r8s9t0abc
                minLength: 1
    OwnedPhoneNumber:
      type: object
      required:
        - id
        - phoneNumber
        - capabilities
        - status
        - pricing
        - createdAt
        - regulatoryStatus
      properties:
        id:
          type: string
        phoneNumber:
          type: string
          example: '+15551234567'
        name:
          type: string
          description: Optional custom name for the phone number.
        capabilities:
          type: array
          items:
            type: string
          example:
            - sms
            - voice
        status:
          $ref: '#/components/schemas/PhoneNumberStatus'
        pricing:
          $ref: '#/components/schemas/OwnedPhoneNumberPricing'
        regulatoryStatus:
          type: string
          enum:
            - approved
            - pending_review
            - rejected
          description: >-
            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.
        nextRenewalDate:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    ChannelEntry:
      type: object
      required:
        - address
        - type
        - active
        - v1SenderId
      description: One place the project can send from, on one transport.
      properties:
        address:
          type: string
          description: >-
            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`).
          example: '+13125551212'
        type:
          $ref: '#/components/schemas/ChannelType'
        active:
          type: boolean
          description: >-
            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:
          type: string
          description: >-
            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`).
    Error:
      type: object
      required:
        - code
        - message
      description: >-
        Every v2 error has this shape at the root. There is no `{error: {...}}`
        wrapper anywhere in v2. A missing resource is `404 not_found` with the
        message `<Resource> not found`; an address the project does not have is
        `404 not_found`, `None of this project's channels has the address
        <address>.`, with the address in `details` under the name the request
        used (`from` or `address`). A `message` is written for the caller and
        never carries a stack trace or a source path. A body that is not JSON is
        `400 invalid_json`, with `details.errors` (`path: []`, the parse error
        in `message`).
      properties:
        code:
          type: string
          example: invalid_request
        message:
          type: string
        details:
          type: object
          additionalProperties: true
          description: >-
            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.
          properties:
            errors:
              type: array
              items:
                type: object
                required:
                  - path
                  - message
                properties:
                  path:
                    type: array
                    items:
                      oneOf:
                        - type: string
                        - type: integer
                  message:
                    type: string
                  code:
                    type: string
                additionalProperties: true
    PhoneNumberType:
      type: string
      enum:
        - local
        - national
        - tollFree
        - mobile
      description: >-
        Type of phone number. `mobile` is stocked in countries where no
        geographic (`local`) or non-geographic (`national`) inventory exists,
        and in several markets it is the only type that can receive SMS.
    PhoneNumberStatus:
      type: string
      description: >-
        Billing state of an owned number, separate from `regulatoryStatus`.
        `pending` is legacy and is not written to numbers today.
      enum:
        - active
        - suspended
        - pending
        - releasing
        - released
    OwnedPhoneNumberPricing:
      type: object
      properties:
        monthlyPrice:
          type: number
          description: Monthly price in USD.
        upfrontCost:
          type: number
          description: One-time purchase cost in cents.
        monthlyCost:
          type: number
          description: Monthly cost in cents.
        isFreeNumber:
          type: boolean
          description: Whether this is a free number.
    ChannelType:
      type: string
      description: A transport a message travels on.
      enum:
        - sms
        - sms_oneway
        - whatsapp
        - telegram
        - email
        - instagram
        - messenger
        - voice
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

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