Beta. Every endpoint on this page is live and documented as shipped. Shapes can still change before general availability.
Overview
The Phone Numbers API lists the numbers your organization owns, searches carrier inventory, rents new numbers and releases numbers you no longer need.
Base URL: https://api.hirefinn.ai/api/v1. Auth: Authorization: Bearer finn_live_.... See authentication.
Assigning a number to a Finn is done by deploying. There is no assign or unassign endpoint. To answer calls on a number, create an inbound deployment for the Finn and the number; to dial from a number, use it in an outbound deployment. The number is freed when you stop the deployment or when it ends as completed or error, and can then be deployed again. See api-deployments.
Renting and releasing have real effects:
- Renting debits your wallet when the request is accepted, then provisions the number asynchronously.
- Releasing gives no refund. The number goes back to the carrier and is deleted from your organization.
Endpoints
| Method | Path | Rate limit (per API key) |
|---|---|---|
GET | /phone_numbers | 300 per minute, shared with GET /phone_numbers/{id} |
GET | /phone_numbers/{id} | Same bucket |
GET | /phone_numbers/available | 30 per minute. Each search is a live carrier query. |
POST | /phone_numbers | 10 per minute, shared with DELETE /phone_numbers/{id} |
DELETE | /phone_numbers/{id} | Same bucket |
The phone number object
{
"id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03",
"phone_number": "+918035731234",
"status": "Live",
"provider": "plivo",
"country": "IN",
"capabilities": { "voice": true, "sms": false, "mms": false },
"allocation": {
"finn_id": "8f14e45f-ceea-467a-9f6a-1c0e5b2a77d1",
"call_type": "inbound"
},
"created_at": "2026-09-02T11:20:41.000Z",
"updated_at": "2026-09-02T11:21:05.000Z"
}
| Field | Type | Notes |
|---|---|---|
id | string | UUID. Use it as phone_number_id in api-deployments. |
phone_number | string | The number as stored, normally E.164. |
status | string | As reported by the carrier. Rented numbers are Live once provisioned. Compare case-insensitively; a number whose status is pending or cancelled cannot be deployed. |
provider | string or null | Carrier: plivo, twilio or exotel. |
country | string or null | ISO 3166-1 alpha-2 code. |
capabilities | object | voice, sms and mms, each true, false or null when unknown. |
allocation | object or null | The Finn the number is currently assigned to through a running deployment, with finn_id and call_type (inbound or outbound). null when unassigned. It can still name a Finn whose deployment has already ended; that number can be deployed again. |
created_at | string | ISO 8601. |
updated_at | string | ISO 8601. |
List numbers
GET /phone_numbers
Returns numbers owned by your organization, newest first.
| Query parameter | Type | Default | Notes |
|---|---|---|---|
limit | integer | 50 | 1 to 200. Out-of-range values are clamped. |
offset | integer | 0 | Number of items to skip. |
curl "https://api.hirefinn.ai/api/v1/phone_numbers?limit=50" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": [
{
"id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03",
"phone_number": "+918035731234",
"status": "Live",
"provider": "plivo",
"country": "IN",
"capabilities": { "voice": true, "sms": false, "mms": false },
"allocation": null,
"created_at": "2026-09-02T11:20:41.000Z",
"updated_at": "2026-09-02T11:21:05.000Z"
}
],
"limit": 50,
"offset": 0
}
There is no total count. Page with offset until a page returns fewer than limit items.
Retrieve a number
GET /phone_numbers/{id}
curl https://api.hirefinn.ai/api/v1/phone_numbers/6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03 \
-H "Authorization: Bearer $FINN_API_KEY"
Returns { "success": true, "data": { ... } } with one phone number object.
Search available numbers
GET /phone_numbers/available
| Query parameter | Type | Required | Notes |
|---|---|---|---|
country | string | yes | 2-letter ISO country code, for example IN or US. |
search | string | no | Up to 20 characters, passed to the carrier to filter numbers, for example by digits. |
provider | string | no | plivo, twilio or exotel. Only exotel changes the search: it searches Exotel inventory. plivo and twilio are accepted but don't pick the carrier: India (IN) is searched on Plivo and every other country on Twilio. When omitted, or set to plivo or twilio, the search follows that country rule, except that omitting provider searches Exotel when your organization's default carrier is Exotel. |
curl "https://api.hirefinn.ai/api/v1/phone_numbers/available?country=US&search=415" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": [
{
"phone_number": "+14155550123",
"country": "US",
"region": "CA",
"locality": "San Francisco",
"number_type": "local",
"capabilities": { "voice": true, "sms": true, "mms": false },
"price": null,
"currency": null
}
]
}
| Field | Type | Notes |
|---|---|---|
phone_number | string | Pass this unchanged to POST /phone_numbers. |
country | string or null | ISO country code. |
region | string or null | Region or state. |
locality | string or null | City. |
number_type | string or null | Carrier number type, for example local. |
capabilities | object | voice, sms, mms: true, false or null. |
price | number or null | Rental price in currency. Set only for Exotel numbers, which are priced per number in INR. For other carriers it is null; the amount charged is returned by POST /phone_numbers. |
currency | string or null | INR when price is set. |
The list is not paginated. When the carrier has nothing matching, data is an empty array.
Some searches require your organization's compliance application to be approved, otherwise the search returns 409 compliance_required: searching India (IN) inventory, searching with provider=exotel, and searching without provider when your organization's default carrier is Exotel. Organizations with an approved application, or that already have a carrier subaccount, can search. Compliance is completed in the dashboard under Settings → Compliance, not through the API; the error's action_url (...settings?tab=telephony-account) opens that tab.
Rent a number
POST /phone_numbers
| Field | Type | Required | Notes |
|---|---|---|---|
phone_number | string | yes | A number from GET /phone_numbers/available. 6 to 15 digits with an optional +; spaces, dashes and parentheses are stripped. |
provider | string | no | plivo, twilio or exotel. Pass exotel to rent an Exotel number. For other numbers the carrier is your organization's configured carrier, or Plivo for +91 and Twilio otherwise. |
capabilities | object | no | { "voice": true, "sms": false, "mms": false }. Each value is a boolean; anything else is treated as unspecified. |
curl -X POST https://api.hirefinn.ai/api/v1/phone_numbers \
-H "Authorization: Bearer $FINN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+14155550123",
"capabilities": { "voice": true }
}'
Response 202 Accepted:
{
"success": true,
"data": {
"status": "provisioning",
"phone_number": "+14155550123",
"transaction_id": "txn_1759740062114_4f9c2a1e",
"price": 2.5,
"currency": "USD",
"credits_debited": 25,
"remaining_credits": 4175
}
}
| Field | Notes |
|---|---|
status | Always provisioning. |
transaction_id | Reference for this purchase. Quote it to support. |
price, currency | The rental price charged. |
credits_debited | Credits taken from the wallet. The ledger entry has type debit_phone_number; see api-wallet. |
remaining_credits | Wallet balance after the debit. |
What happens:
- The wallet is debited immediately.
- The carrier purchase runs in the background. The response does not include the number's
id. - Poll
GET /phone_numbersuntil the number appears, then use itsid. - If the carrier purchase fails, the debit is refunded (ledger type
credit_refund_phone_number) and the number never appears in the list.
Requirements:
- Your organization's compliance application must be approved, otherwise
403 compliance_required. Submit it in the dashboard under Settings → Compliance; the error'saction_urlopens that tab. See api-errors. - The rental is attributed to the user who created the API key. That user must still be a member of your organization, with an email on record, otherwise
409 api_key_owner_required; create a new key from the dashboard. - Organizations in proof-of-concept mode use numbers provided by Finn and cannot rent (
403 rental_unavailable).
There is no idempotency key. Retrying a rental for the same number doesn't charge you twice: the wallet is debited only once per number for your organization. A retry does record another purchase and start the number's setup again, so check GET /phone_numbers before you retry.
Release a number
DELETE /phone_numbers/{id}
| Query parameter | Type | Notes |
|---|---|---|
force | string | true removes the number's assignment records (current and past) before releasing. |
curl -X DELETE "https://api.hirefinn.ai/api/v1/phone_numbers/6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"success": true,
"data": { "id": "6b1f0c2e-3d4a-4f5b-9c8d-2e7a1b9f4c03", "released": true }
}
Releasing returns the number to the carrier and deletes it from your organization. There is no refund.
The API refuses to release:
- a number used by a deployment that hasn't ended, whether it's the deployment's only number or one of the numbers in its
number_pool:409 number_in_use. Stop the deployment, then release the number. - a number that has ever been a deployment's
phone_number_id, even if that deployment is stopped or completed:409 number_in_use. Contact Finn support to release it. Parallel deployments (max_parallel_callsabove 1) keep their number only innumber_pool, so once one of them ends, it no longer blocks the release. - a number with assignment records, unless you pass
force=true:409 number_in_use.
Errors
| Status | error | Endpoint | When |
|---|---|---|---|
| 400 | invalid_request | all | id is not a UUID; country is not 2 letters; search is too long; provider is not a listed carrier; phone_number is malformed; or capabilities is not an object. |
| 400 | price_currency_mismatch | POST | The number is priced in a different currency from your wallet. |
| 401 | api_key_required | all | No Bearer finn_live_... key. |
| 401 | invalid_api_key | all | Key unknown or revoked. |
| 402 | insufficient_credits | POST | The wallet cannot cover the rental. The body adds required_credits and available_credits. |
| 403 | compliance_required | POST | Your organization's compliance application is not approved. Complete it at action_url. |
| 403 | rental_unavailable | POST | Your organization is in proof-of-concept mode. |
| 404 | phone_number_not_found | GET one, DELETE | No number with that ID in your organization. |
| 409 | compliance_required | GET /available | India (IN) or Exotel inventory requested without an approved compliance application. Complete it at action_url. |
| 409 | api_key_owner_required | POST | The key's creator is no longer a member of your organization or has no email. |
| 409 | number_in_use | DELETE | Used by a deployment that hasn't ended (as its number or in its number_pool), ever used as a deployment's phone_number_id, or has assignment records and force was not true. |
| 429 | rate_limited | all | Over the endpoint's per-minute limit. |
| 500 | rent_failed | POST | The number's setup couldn't be started. The debit was refunded; the body includes transaction_id. |
| 500 | internal_error | all | Unexpected server error. |
| 502 | provider_unavailable | GET /available, DELETE | The carrier could not be reached. Retry later. |
| 502 | price_unavailable | POST | The rental price could not be confirmed with the carrier. Nothing was charged. Retry later. |
Error bodies have the shape { "error": "...", "message": "..." }; internal_error has no message. See api-errors.