---
title: "Buy a phone number"
method: POST
path: "/phone-numbers/purchase"
tags: ["Phone Numbers"]
---

# Buy a phone number

`POST /phone-numbers/purchase`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/phone-numbers/purchase`
- Auth: HTTP bearer

Buy a number from Twilio or Telnyx. THIS SPENDS CREDITS AND REAL MONEY.

Charged on PURCHASE, not on first use: the handler holds credits, buys the
number from the carrier, stores it, registers it with the voice platform
and only then converts the hold to a charge. A number bought and never
dialled has still cost.

Not idempotent by number - buy the same number twice and the second call
is a 409 rather than a second charge, but a retry after a timeout you
cannot see the outcome of may have already succeeded. Send an
`Idempotency-Key` header; every POST on this API honours it.

`402` means insufficient credits and is the one failure worth handling
distinctly - the number was not bought and nothing was charged.

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- PurchasePhoneRequest: Buy ONE number from the provider. THIS SPENDS CREDITS AND REAL MONEY. Mirrors `phone_numbers.interfaces.fastapi.dtos.PurchasePhoneRequest` field-for-field. `capabilities` is deliberately absent from both: it was dropped in the app on 2026-06-02 because the handler never read it and sending it silently 422'd.
  - `phone_number` string, required: The number to buy, E.164 (e.g. `+14155550123`). Search for one first.
  - `provider` 'twilio' | 'telnyx', required: Which carrier to buy it from.
  - `assignee` string, nullable: Who the number belongs to. Defaults to the identity behind the API key, matching the app - it is never taken from the body's organisation, because there is no organisation in the body.
  - `dialer` boolean: True provisions a dialer number; false provisions a voice-AI number.
  - `work` boolean: True makes this someone's personal work phone - provisioned like a dialer number but tagged `work`, and never part of the outbound caller-ID rotation pool.

## Response `201`

Successful Response

- ApiResponsePhoneNumberPayload
  - `data` PhoneNumberPayload, required: An open envelope for a purchase result, a release, or an A2P job. Open on purpose. A purchase returns the stored row (whose capability and provider-metadata keys differ per provider), a workflow start returns a `workflow_id`, and the A2P reconcile returns per-number outcomes that grow as carriers add statuses. Pinning it would silently drop whatever the provider reported that this contract had not caught up on.
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
