---
title: "Purchase or Import Phone Number"
method: POST
path: "/v1/agent/phone-numbers"
tags: ["Phone Numbers"]
---

# Purchase or Import Phone Number

`POST /v1/agent/phone-numbers`

## Request

- Base URL: `https://api.fish.audio`
- URL: `https://api.fish.audio/v1/agent/phone-numbers`
- Auth: HTTP bearer

`provider` discriminates two variants. `twilio` buys a number from the
managed inventory: it lands in your default workspace, any `agent_id` you
bind must live there too, and billing is the monthly price charged in
daily slices; the first day is charged before anything is bought (402
costs you nothing) and the daily run advances it from there. `sip`
imports a number you already own at your carrier: point your trunk's
origination at our SIP host, provide inbound authentication (digest
and/or source CIDRs) and optionally a termination host so the number can
place calls; nothing is rented and there is no monthly fee. Either way,
409 means the number is already on the platform; 502 means the provider
or trunk provisioning refused, and the number stays visible with status
`error` and is safe to release.

## Request body

- union
  - PublicPhoneNumberPurchasePayload
    - `provider` 'twilio', required: `twilio` buys from the managed inventory (supports call transfer); `sip` imports a number you already own at your carrier; see the `sip` request variant.
    - `phone_number` string, required: E.164 number from `GET /v1/agent/available-phone-numbers`, e.g. +14155550123.
    - `label` string
    - `agent_id` string, nullable: Bind an agent to answer inbound calls right away.
  - PublicSipNumberImportPayload: The `sip` variant of POST /v1/agent/phone-numbers: import a number that stays at your carrier. Point your trunk's origination at our SIP host, give inbound at least one authentication factor (digest and/or source CIDRs), and optionally a termination host + credentials (plus custom `X-` headers for every outbound INVITE) so the number can place calls. Nothing is rented: carrier charges stay on your account, and imported numbers carry no telephony charges at all: no monthly fee, no phone surcharge, no transfer fees; you pay agent minutes only.
    - `inbound_auth_username` string
    - `inbound_auth_password` string
    - `inbound_allowed_addresses` string[]
    - `termination_uri` string
    - `termination_transport` 'auto' | 'udp' | 'tcp' | 'tls'
    - `termination_auth_username` string
    - `termination_auth_password` string
    - `termination_headers` object
    - `phone_number` string, required
    - `label` string
    - `agent_id` string, nullable
    - `provider` 'sip', required

## Response `201`

Document created, URL follows

- object
  - `phone_number_id` string, required
  - `workspace_id` string, required
  - `phone_number` string, required: E.164, e.g. +14155550123.
  - `provider` 'livekit' | 'twilio' | 'sip' | 'whatsapp', required
  - `label` string
  - `agent_id` string, nullable, required: Inbound calls route to this agent; unbound numbers ring busy.
  - `transfer_caller_id` 'agent_number' | 'original_caller', nullable: Managed `twilio` numbers: the caller ID a transfer target sees, on cold and warm transfers alike. `agent_number` shows this number, `original_caller` shows the caller's own number (a warm transfer falls back to this number when the caller withholds theirs). `null` for imported `sip` numbers, whose carrier decides.
  - `cold_transfer_use_original_caller` boolean, nullable: Deprecated, read `transfer_caller_id`. True when it is `original_caller`.
  - `cold_transfer_use_original_caller_effective` boolean, nullable: Deprecated, read `caller_id_sync_status`. Whether the carrier has confirmed the original caller policy for cold transfers, `null` while unknown.
  - `caller_id_sync_status` 'synced' | 'pending' | 'error' | 'carrier_managed': `synced` once the carrier confirmed the policy, `pending` while it is being applied, `error` when the last attempt failed (send `retry_caller_id_sync`), `carrier_managed` for imported `sip` numbers.
  - `caller_id_sync_error` string: Why the last synchronization failed; empty otherwise.
  - `status` 'provisioning' | 'active' | 'error' | 'released', required
  - `status_detail` string: What failed when status is `error`; empty otherwise.
  - `supports_outbound` boolean: Whether the number can place calls (outbound and warm-transfer consult legs): managed `twilio` always, imported `sip` only when a termination was configured.
  - `termination_uri` string, nullable: Imported `sip` numbers: the customer trunk's termination host.
  - `inbound_auth_username` string, nullable: Imported `sip` numbers: the inbound digest username. Passwords are never echoed.
  - `inbound_allowed_addresses` string[]: Imported `sip` numbers: allowed source IPs/CIDRs.
  - `termination_transport` string, nullable: Imported `sip` numbers: termination transport.
  - `termination_auth_username` string, nullable: Imported `sip` numbers: the termination digest username. Passwords are never echoed.
  - `termination_headers` object: Imported `sip` numbers: custom SIP headers added to every outbound INVITE through the termination.
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `401`: No permission -- see authorization schemes
- `402`: No payment -- see charging schemes
- `403`: Request forbidden -- authorization will not help
- `404`: Nothing matches the given URI
- `409`: Request conflict
- `422`
- `502`: Invalid responses from another server/proxy
- `503`: The server cannot process the request due to a high load

## Changes

- **2026-11-03** `42ac421f7a12`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/fish/apis/fishaudio-openapi/changes/v1/agent/phone-numbers/post.md)

---

[API](https://skmtc.dev/fish/apis/fishaudio-openapi.md) - [All operations](https://skmtc.dev/fish/apis/fishaudio-openapi/llms.txt) - [OpenAPI document](https://skmtc.dev/fish/apis/fishaudio-openapi/revisions/42ac421f7a12?raw)
