> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.agentphone.ai/documentation/guides/phone-numbers/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.agentphone.ai/_mcp/server. # Phone numbers > Provision, manage, and release SMS- and voice-enabled phone numbers Phone numbers are carrier-grade, SMS- and voice-enabled numbers. You can provision new numbers, attach them to agents, list existing numbers, retrieve messages for a number, and release numbers when no longer needed. > **Note** > > **SMS compliance:** Receiving inbound SMS works out of the box. To send outbound SMS, US carriers require 10DLC (10-Digit Long Code) registration. Fill out the [registration form](https://agentphone.ai/register), it takes about 5 minutes and we submit everything to the carriers for you. If you can't figure it out, [schedule a call](https://calendar.app.google/kJQSTCyNYTphvCGm7) and we'll handle the registration for you. Voice calls (inbound and outbound) are not affected and work immediately. ## Search available numbers Preview purchasable numbers before buying one. ``` GET /v1/numbers/available ``` ### Query parameters | Parameter | Description | | ---------- | -------------------------------------------------------------------------------------------------------------- | | `areaCode` | 3-digit area code, e.g. `415`. The most broadly supported filter. | | `city` | City name. Pair with `state`. Support varies by account. | | `state` | 2-letter state code, e.g. `CA`. Pair with `city` for consistent results; standalone support varies by account. | | `zip` | 5-digit ZIP. Support varies by account. | | `country` | `US` or `CA`. Defaults to `US`. | | `limit` | Max results, 1 to 30. Defaults to 10. | Searching with no criteria returns a page of whatever is in stock. Giving a Canadian area code searches Canada even though `country` defaults to `US`. Criteria support varies by account and there is no single rule that holds everywhere: * `areaCode` is the most broadly supported filter. * `city`, `state` and `zip` work on some accounts and not others. Where they aren't supported they are **ignored rather than rejected**, so check what comes back instead of assuming the filter applied. * `state` on its own is honored on some accounts and rejected with `400` on others. Pair it with `city` for consistent behavior. > **Note** > > Number search isn't available on every account. When it isn't, this endpoint returns `404` (or `422` if it's enabled but not yet configured). > > That doesn't block you from buying a number. `POST /v1/numbers` accepts an `areaCode` and picks one for you on any account, so search is a convenience rather than a requirement. Contact support if you want search enabled. ### Example ```bash curl "https://api.agentphone.ai/v1/numbers/available?areaCode=415&limit=5" \ -H "Authorization: Bearer $AGENTPHONE_API_KEY" ``` ```json { "data": [ { "phoneNumber": "+14155550123", "city": "San Francisco", "state": "CA", "rateCenter": "SNFC CNTRL", "areaCode": "415" } ], "widened": false } ``` `phoneNumber` is the only guaranteed field. `city`, `state` and `rateCenter` are `null` when the inventory source returns no per-number detail, so key off `phoneNumber` and treat the rest as best-effort labels. `widened: true` means your exact criteria were out of stock and these are nearby alternatives instead. Check it before assuming you got what you asked for. ### Buying a number you found Pass the same `phoneNumber` to `POST /v1/numbers`: ```bash curl -X POST "https://api.agentphone.ai/v1/numbers" \ -H "Authorization: Bearer $AGENTPHONE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"phoneNumber": "+14155550123"}' ``` > **Warning** > > **Search results are not reserved.** This is live inventory, so a number can be bought by someone else between your search and your purchase. When that happens the purchase returns `409` with fresh alternatives in the same shape as the search response. Pick another and retry rather than treating it as fatal. > **Warning** > > **Search and buy under the same account.** Each account and sub-account has its own inventory. A number only buys if it came from `GET /v1/numbers/available` called with the same API key and `X-Sub-Account-Id` header you then pass to `POST /v1/numbers`. A number found under the parent key, under a different sub-account, or from another vendor's search will always return `409` with `NUMBER_UNAVAILABLE`, no matter how many times you retry it. If you don't need a specific number, send an `areaCode` instead and the sub-account gets the next available number in that code. ```python import httpx H = {"Authorization": f"Bearer {API_KEY}"} def buy_in_area_code(area_code: str) -> dict: r = httpx.get( "https://api.agentphone.ai/v1/numbers/available", params={"areaCode": area_code, "limit": 5}, headers=H, ) r.raise_for_status() for entry in r.json()["data"]: resp = httpx.post( "https://api.agentphone.ai/v1/numbers", json={"phoneNumber": entry["phoneNumber"]}, headers=H, ) if resp.status_code == 409: continue # taken between search and purchase, try the next one resp.raise_for_status() return resp.json() raise RuntimeError(f"every candidate in {area_code} was taken") ``` Search is rate limited to 30 requests per minute per account, and purchase to 20. ## Create number Provision a new SMS-enabled phone number. ``` POST /v1/numbers ``` ### Request body | Field | Type | Required | Default | Description | | ---------- | -------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `country` | string | No | `"US"` | Two-letter country code for the number (e.g., `"US"`, `"CA"`) | | `areaCode` | string or null | No | null | Preferred area code (US/CA only, e.g., `"415"`). If none are available in that area code, you'll get another number in the same country. | | `agentId` | string or null | No | null | Optionally attach the number to an agent immediately | ### Example ```bash curl -X POST "https://api.agentphone.ai/v1/numbers" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "country": "US", "areaCode": "415", "agentId": "agt_abc123" }' ``` ```json { "id": "num_xyz789", "phoneNumber": "+15551234567", "country": "US", "status": "active", "agentId": "agt_abc123", "createdAt": "2025-01-15T10:45:00Z" } ``` ## List numbers List all phone numbers for this project. ``` GET /v1/numbers ``` ### Query parameters | Parameter | Type | Required | Default | Description | | --------- | ------- | -------- | ------- | ------------------------------------- | | `limit` | integer | No | 20 | Number of results to return (max 100) | | `offset` | integer | No | 0 | Number of results to skip (min 0) | ### Example ```bash curl -X GET "https://api.agentphone.ai/v1/numbers?limit=10&offset=0" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Look up a number Check line-type intelligence for any phone number before you message it: whether it is a mobile, landline, or VoIP line, its country, and whether the handset supports RCS. Useful for keeping agents from spending messages on numbers that can never receive them. ``` GET /v1/numbers/lookup ``` Billed at \$0.009 per number looked up. ### Query parameters | Parameter | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------------------------------- | | `phoneNumbers` | string | Yes | Comma-separated E.164 numbers to look up, up to 25 per request | ### Example ```bash curl -X GET "https://api.agentphone.ai/v1/numbers/lookup?phoneNumbers=%2B14155551234,%2B15551230000" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```json { "data": [ { "phoneNumber": "+14155551234", "lineType": "mobile", "country": "US", "rcsEnabled": true }, { "phoneNumber": "+15551230000", "lineType": "landline", "country": "US", "rcsEnabled": false } ] } ``` `lineType` is `"mobile"`, `"landline"`, or `"voip"`; fields are `null` when a number cannot be resolved. Accounts with a negative balance receive a `402` before any lookup runs. ## Delete number (release) Release (delete) a phone number. This action: 1. Releases the number back to the carrier pool 2. Marks the number as `"released"` in the database 3. Keeps all messages and conversation history for audit purposes > **Warning** > > This action is irreversible. The number cannot be recovered once released. ``` DELETE /v1/numbers/{number_id} ``` ### Example ```bash curl -X DELETE "https://api.agentphone.ai/v1/numbers/num_xyz789" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ## Get messages for number Get messages for a specific phone number. Supports cursor-based pagination via `before`/`after` timestamps. ``` GET /v1/numbers/{number_id}/messages ``` ### Query parameters | Parameter | Type | Required | Default | Description | | --------- | ------------------------- | -------- | ------- | ------------------------------------------------ | | `limit` | integer | No | 50 | Number of messages to return (max 200) | | `before` | string (datetime) or null | No | null | Return messages before this timestamp (ISO 8601) | | `after` | string (datetime) or null | No | null | Return messages after this timestamp (ISO 8601) | ### Example ```bash curl -X GET "https://api.agentphone.ai/v1/numbers/num_xyz789/messages?limit=10" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ```json { "data": [ { "id": "msg_001", "from_": "+15559876543", "to": "+15551234567", "body": "Hi, I need help with my order", "receivedAt": "2025-01-15T12:00:00Z" } ], "hasMore": false } ``` > Provision, manage, and release SMS- and voice-enabled phone numbers