Phone numbers
Buy, connect and route phone numbers. Several of these spend money immediately.
Get phone number overview
Section titled “Get phone number overview”GET /telephony/overview/{organisationId}
One call that returns your provider connections, your active phone numbers (with inbound and outbound agents), and, depending on the key’s other scopes, your wallet balance and call rates. Wallet fields are null unless the key also has wallet:read, and rates are null unless it also has billing:read.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/overview/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "featureEnabled": true, "dodoEnabled": true, "paid": true, "wallet": { "balance": 42.5, "currency": "USD", "hasPaid": true }, "rates": { "baseRatePerMinute": 0.05, "countryProviderRates": { "IN": { "plivo": 0.04 } } }, "integrations": [ { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "plivo", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "config": { "label": "Plivo Account", "connected_at": "2026-09-20T10:15:00.000Z", "plivo_app_id": "12345678901234567", "plivo_auth_id": "configured", "plivo_auth_token": "configured" } } ], "numbers": [ { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "+14155550123", "status": "ACTIVE", "routingStatus": "ASSIGNED", "capabilities": { "voice": true, "sms": false }, "agentId": "cmf3k2j9x0003abcd1234efgh", "agent": { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, "assignedAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" } ], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_purchase", "type": "local", "region": "California" }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "outboundAgents": [ { "id": "cmf3k2j9x0006abcd1234efgh", "name": "Payment reminder" } ] } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 429 | Rate limited |
List phone numbers
Section titled “List phone numbers”GET /telephony/numbers/{organisationId}
Lists your organisation’s active phone numbers, newest first, with the agent that answers each one (inbound) and the agents that dial out from it (outbound caller ID). No pagination.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/numbers/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": [ { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "+14155550123", "status": "ACTIVE", "routingStatus": "ASSIGNED", "capabilities": { "voice": true, "sms": false }, "agentId": "cmf3k2j9x0003abcd1234efgh", "agent": { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, "assignedAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" } ], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_purchase", "type": "local", "region": "California" }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "outboundAgents": [ { "id": "cmf3k2j9x0006abcd1234efgh", "name": "Payment reminder" } ] } ]}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 429 | Rate limited |
Connect a Twilio account
Section titled “Connect a Twilio account”POST /telephony/twilio/connect
Connects your own Twilio account and imports every voice number on it (all pages). No wallet funding is needed. Credentials are stored encrypted and never returned. On a reconnect of the same account you may omit accountSid and authToken and the stored ones are reused; a different accountSid needs its own authToken. A reconnect reconciles the account: numbers still there are updated, numbers gone from it are deactivated (only when the whole account was read). Switching to a different Twilio account takes Foan off the previous account’s numbers and retires them, and is refused with 409 while any agent still answers on or dials out from one of them. amd is kept as it was when omitted on a reconnect. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
accountSid |
string | no | Your Twilio Account SID. Optional when this organisation already has this account connected. |
authToken |
string | no | Your Twilio Auth Token. Optional when reconnecting the account already connected (same or omitted accountSid). |
label |
string | no | Display name for the connection. Defaults to the existing label, then “Twilio Account”. |
amd |
boolean | no | Answering machine detection for calls through this account. When omitted, a reconnect of the same account keeps the current setting; a new account defaults to false. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/twilio/connect" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","accountSid":"YOUR_TWILIO_ACCOUNT_SID","authToken":"YOUR_TWILIO_AUTH_TOKEN","label":"Main Twilio","amd":false}'Response 201
{ "success": true, "data": { "integration": { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "twilio", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "config": { "label": "Main Twilio", "amd": false, "connected_at": "2026-09-24T08:30:00.000Z", "account_sid": "configured", "auth_token": "configured" } }, "reusedStoredCredentials": false, "imported": 3, "updated": 0, "deactivated": 0 }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, Account SID or Auth Token missing (or the stored ones are incomplete), or Twilio rejected the credentials. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Twilio self-serve is not enabled on the platform. |
| 404 | Organisation not found. |
| 409 | Switching accounts while agents still use numbers on the previous Twilio account, or when another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Search Twilio numbers to buy
Section titled “Search Twilio numbers to buy”POST /telephony/twilio/search
Searches available voice numbers on your connected Twilio account. Tries Local, then Mobile, then TollFree, and returns the first type that has results (up to 20). Read-only at Twilio, but needs a funded wallet and a connected Twilio account.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
country |
string | no | Two-letter country code. Defaults to US. |
areaCode |
string | no | Area code filter. |
contains |
string | no | Digits or pattern the number should contain. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/twilio/search" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","country":"US","areaCode":"415"}'Response 200
{ "success": true, "data": { "country": "US", "type": "Local", "numbers": [ { "phoneNumber": "+14155550123", "friendlyName": "(415) 555-0123", "locality": "San Francisco", "region": "CA", "capabilities": { "voice": true, "SMS": true, "MMS": false } } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or the stored Twilio credentials are incomplete. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded. |
| 403 | Missing scope, foreign organisation, or Twilio self-serve is not enabled. |
| 404 | No Twilio account is connected (“twilio integration not found”). |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Buy a Twilio number
Section titled “Buy a Twilio number”POST /telephony/twilio/purchase
Buys the given number immediately on your own connected Twilio account (Twilio bills that account) and points its voice webhook at Foan. There is no confirm step or dry run. Your Foan wallet must be funded above the minimum balance (402 otherwise), but this call does not debit the wallet.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
phoneNumber |
string | yes | E.164 number from a Twilio search result. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/twilio/purchase" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","phoneNumber":"+14155550123"}'Response 201
{ "success": true, "data": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "twilio", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "friendlyName": "(415) 555-0123", "status": "ACTIVE", "routingStatus": "PURCHASED", "capabilities": { "voice": true, "sms": false }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "twilio_purchase", "capabilities": { "voice": true } }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, the stored Twilio credentials are incomplete, or Twilio rejected the purchase or credentials. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded or below the minimum available balance (default 1). |
| 403 | Missing scope, foreign organisation, or Twilio self-serve is not enabled. |
| 404 | No Twilio account is connected. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Connect an Exotel account
Section titled “Connect an Exotel account”POST /telephony/exotel/connect
Connects your own Exotel account and imports its incoming numbers. Needs a funded wallet (402 otherwise). Credentials are stored encrypted and never returned. Reconnecting replaces them.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
accountSid |
string | yes | Your Exotel account SID. |
apiKey |
string | yes | Your Exotel API key. |
apiToken |
string | yes | Your Exotel API token. |
appId |
string | yes | The Exotel app (flow) ID calls should use. |
subdomain |
string: api.exotel.com, api.in.exotel.com | no | Exotel API host: api.exotel.com (default) or api.in.exotel.com. |
label |
string | no | Display name. Defaults to “Exotel Account”. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/exotel/connect" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","accountSid":"YOUR_EXOTEL_ACCOUNT_SID","apiKey":"YOUR_EXOTEL_API_KEY","apiToken":"YOUR_EXOTEL_API_TOKEN","appId":"YOUR_EXOTEL_APP_ID","subdomain":"api.in.exotel.com","label":"Exotel India"}'Response 201
{ "success": true, "data": { "integration": { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "exotel", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "config": { "label": "Exotel India", "exotel_app_id": "YOUR_EXOTEL_APP_ID", "exotel_subdomain": "api.in.exotel.com", "connected_at": "2026-09-24T08:30:00.000Z", "exotel_sid": "configured", "exotel_key": "configured", "exotel_token": "configured" } }, "synced": { "remoteCount": 2, "synced": 2, "skipped": 0 } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, subdomain not allowed, or Exotel rejected the credentials. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded. |
| 403 | Missing scope, foreign organisation, or self-serve telephony is not enabled. |
| 409 | Another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited by Foan (about 20/min per key plus a stricter sensitive-route limit), or Exotel’s own rate limit was reached. |
| 502 | Could not reach Exotel or it returned an invalid response. |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Sync Exotel numbers
Section titled “Sync Exotel numbers”POST /telephony/exotel/sync
Re-reads the incoming numbers on your connected Exotel account and creates or updates them in Foan. Needs a funded wallet and an existing Exotel connection.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/exotel/sync" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "synced": { "remoteCount": 4, "synced": 3, "skipped": 1 } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, stored Exotel credentials incomplete, or Exotel rejected them. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded. |
| 403 | Missing scope, foreign organisation, or self-serve telephony is not enabled. |
| 404 | No Exotel account is connected. |
| 409 | Another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited by Foan, or Exotel’s own rate limit was reached. |
| 502 | Could not reach Exotel. |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Connect a Plivo account
Section titled “Connect a Plivo account”POST /telephony/plivo/connect
Connects your own Plivo account and imports every number on it (or only the one you name in phoneNumber). Foan creates or updates a Plivo application on your account and points each imported number at it, so their calls start routing to Foan. A number that already answers through another Plivo application is imported with routingStatus NEEDS_SETUP and left alone at Plivo until you assign it to an agent; a number you name in phoneNumber is moved across straight away. Numbers on Plivo subaccounts are never imported. No wallet funding is needed and Foan bills nothing for these numbers (Plivo bills you). On a reconnect of the same account you may omit authId and authToken and the stored ones are reused. A whole-account connect also deactivates numbers that are no longer on the account. Switching to a different Plivo account takes Foan off the previous account’s numbers and retires them, and is refused with 409 while any agent still uses one. Foan’s own main Plivo account cannot be connected. Credentials are stored encrypted and never returned. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
authId |
string | no | Your Plivo Auth ID. Optional when this organisation already has this account connected. |
authToken |
string | no | Your Plivo Auth Token. Optional when reconnecting the account already connected (same or omitted authId). |
phoneNumber |
string | no | Optional. Import only this number (E.164) from the account; 404 if the account does not have it. Omit to import the whole account. |
label |
string | no | Display name, also written as the alias of numbers Foan points at its application. Defaults to the existing label, then “Plivo Account”. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/connect" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","authId":"YOUR_AUTH_ID","authToken":"YOUR_AUTH_TOKEN","label":"Support lines"}'Response 201
{ "success": true, "data": { "integration": { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "plivo", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-10-05T09:30:00.000Z", "config": { "label": "Support lines", "connected_at": "2026-10-05T09:30:00.000Z", "plivo_app_id": "12345678901234567", "plivo_auth_id": "configured", "plivo_auth_token": "configured" } }, "reusedStoredCredentials": false, "imported": 2, "updated": 0, "needsSetup": 1, "deactivated": 0, "numbers": [ { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "Support lines", "status": "ACTIVE", "routingStatus": "IMPORTED", "capabilities": { "voice": true, "sms": false }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_sync", "region": "California", "numberType": "local", "voiceEnabled": true }, "createdAt": "2026-10-05T09:30:00.000Z", "updatedAt": "2026-10-05T09:30:00.000Z", "outboundAgents": [] }, { "id": "cmf3k2j9x0007abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550188", "countryCode": "US", "providerNumberId": "14155550188", "friendlyName": "+14155550188", "status": "ACTIVE", "routingStatus": "NEEDS_SETUP", "capabilities": { "voice": true, "sms": true }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_sync", "region": "California", "numberType": "local", "voiceEnabled": true }, "createdAt": "2026-10-05T09:30:00.000Z", "updatedAt": "2026-10-05T09:30:00.000Z", "outboundAgents": [] } ], "number": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "Support lines", "status": "ACTIVE", "routingStatus": "IMPORTED", "capabilities": { "voice": true, "sms": false }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_sync", "region": "California", "numberType": "local", "voiceEnabled": true }, "createdAt": "2026-10-05T09:30:00.000Z", "updatedAt": "2026-10-05T09:30:00.000Z", "outboundAgents": [] } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, Auth ID or Auth Token missing (or the stored ones are incomplete), or Plivo rejected the credentials. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Plivo self-serve is not enabled. |
| 404 | The number named in phoneNumber is not on this Plivo account, or the organisation was not found. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. Also returned for Foan’s own main Plivo account, for switching accounts while agents still use numbers on the previous one, and when another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Search Plivo numbers to buy
Section titled “Search Plivo numbers to buy”POST /telephony/plivo/search
Searches up to 20 available voice numbers on your connected Plivo account, with Plivo’s monthly rental and whether the market needs a compliance (KYC) approval before purchase. Read-only at Plivo. Needs a funded wallet (402 otherwise) and a connected Plivo account of your own. Plivo bills the rental to your Plivo account; Foan does not charge it.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
country |
string | no | Two-letter country code. Defaults to US. |
type |
string: local, mobile, tollfree, national, fixed | no | Number type: local, mobile, tollfree, national or fixed. Defaults to local in US and CA, fixed elsewhere. Unknown values are ignored. |
pattern |
string | no | Digits the number should contain; letters, digits, * and ? are kept, anything else is stripped. |
region |
string | no | Region filter. |
city |
string | no | City filter. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/search" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","country":"US","type":"local","pattern":"415"}'Response 200
{ "success": true, "data": { "country": "US", "type": "local", "complianceRequired": false, "numbers": [ { "phoneNumber": "+14155550123", "providerNumber": "14155550123", "locality": "San Francisco", "region": "California", "type": "local", "monthlyRentalRate": "0.50000", "providerMonthlyRentalRate": "0.50000", "providerMonthlyRentalCurrency": "USD", "customerMonthlyRentalRate": "0.5000", "customerMonthlyRentalCurrency": "USD", "customerMonthlyRentalFxRate": 1, "setupRate": "0.00000", "voiceRate": "0.00850", "restriction": null, "restrictionText": null, "complianceRequired": false, "capabilities": { "voice": true, "sms": true, "mms": false } } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, stored Plivo credentials incomplete, or Plivo rejected them. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded. |
| 403 | Missing scope, foreign organisation, or Plivo self-serve is not enabled. |
| 404 | No Plivo account is connected. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Buy a Plivo number
Section titled “Buy a Plivo number”POST /telephony/plivo/purchase
Buys the number immediately on your own connected Plivo account and points it at Foan. There is no confirm step or dry run. Plivo bills the number and its rental to your Plivo account; Foan records no rental for it and this call does not debit your wallet, but your wallet must be funded above the minimum balance (402 otherwise). The number type and rental are read from Plivo’s inventory, never from the request. Regulated markets (India, restricted numbers, or where Plivo requires it) need an approved compliance application covering that country and number type, checked live with Plivo before buying.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
phoneNumber |
string | yes | Number from a Plivo search result (E.164 or digits). |
country |
string | no | Two-letter country code. Must match the number’s own country or the purchase is refused (503). |
type |
string | no | Accepted but ignored: the regulatory number type is read from Plivo’s inventory, never from the request. |
region |
string | no | Stored on the number for display. |
city |
string | no | Stored on the number for display. |
setupRate |
string | no | Plivo setup rate from the search result, stored for reference. |
restriction |
string | no | restriction from the search result. Any value marks the number as regulated. |
restrictionText |
string | no | restrictionText from the search result. |
complianceApplicationId |
string | no | Optional Plivo compliance application ID to buy against. Must already be linked to your organisation. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/purchase" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","phoneNumber":"+14155550123","country":"US","region":"California","city":"San Francisco","setupRate":"0.00000","restriction":null,"restrictionText":null}'Response 201
{ "success": true, "data": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "+14155550123", "status": "ACTIVE", "routingStatus": "PURCHASED", "capabilities": { "voice": true, "sms": false }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_purchase", "type": "local", "region": "California", "city": "San Francisco" }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, stored Plivo credentials incomplete, the compliance application is not linked to your organisation, or Plivo rejected the request. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded or below the minimum available balance (default 1). |
| 403 | Missing scope, foreign organisation, or Plivo self-serve is not enabled. |
| 404 | No Plivo account is connected. |
| 409 | No approved compliance application covers this number’s country and type, or the one given was refused or covers another market (the message says what to do next). Also returned when this organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 503 | The country or regulatory type could not be confirmed (including a country that does not match the number), or Plivo could not confirm the compliance requirement or approval just now. Nothing was bought; retry later. |
Get Plivo compliance requirements
Section titled “Get Plivo compliance requirements”POST /telephony/plivo/compliance/requirements
Returns the document types and fields Plivo needs before it will sell a number of the given country, number type and end-user type. Read-only at Plivo. Needs a connected Plivo account; no wallet check.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
country |
string | yes | Two-letter country code. |
numberType |
string | no | local, mobile, tollfree, national or fixed. Defaults to local (fixed becomes local in India). |
userType |
string | no | End-user type, business (default) or individual. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/compliance/requirements" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","country":"IN","numberType":"local","userType":"business"}'Response 200
{ "success": true, "data": { "requirement_id": "5f8e1a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b", "country_iso": "IN", "number_type": "local", "user_type": "business", "document_types": [ { "document_type_id": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d", "name": "Business registration: Certificate of Incorporation", "description": "Accepted documents: Certificate of Incorporation, GST Certificate. The first option is used by default.", "proof_required": true, "required_fields": [ { "field_name": "business_name", "friendly_name": "Business name", "required": true }, { "field_name": "unique_identification_number", "friendly_name": "Registration number", "required": true } ] } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, stored Plivo credentials incomplete, or Plivo rejected them. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | No Plivo account connected, or Plivo returned no requirements for that combination. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
List Plivo compliance applications
Section titled “List Plivo compliance applications”POST /telephony/plivo/compliance/applications/list
Lists the Plivo compliance applications linked to your organisation, read live from Plivo, with optional filters. Applications Plivo could not return right now are listed in unavailable rather than silently dropped, so treat the list as incomplete while verificationUnavailable is true.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
country |
string | no | Two-letter country filter. |
numberType |
string | no | Number type filter. |
userType |
string | no | End-user type filter (business or individual). |
status |
string | no | Exact Plivo status filter. |
alias |
string | no | Substring match on the alias. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/compliance/applications/list" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","country":"IN"}'Response 200
{ "success": true, "data": { "compliances": [ { "compliance_id": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809", "alias": "Priya Sharma Foods IN compliance [org:cmf3k2j9x0001abcd1234efgh]", "status": "accepted", "country_iso": "IN", "number_type": "local", "user_type": "business", "rejection_reason": null, "created_at": "2026-09-10T09:00:00Z", "documents": [ { "document_id": "d0c1e2f3-a4b5-4c6d-8e7f-9a0b1c2d3e4f", "document_type_id": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d", "status": "accepted" } ] } ], "unavailable": [], "verificationUnavailable": false, "meta": { "limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1 } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or stored Plivo credentials incomplete. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | No Plivo account is connected. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Submit a Plivo compliance application
Section titled “Submit a Plivo compliance application”POST /telephony/plivo/compliance/applications
Creates and submits a Plivo compliance (KYC) application in one call: creates the end user, uploads each document, creates the application and submits it for Plivo’s review, then links it to your organisation. Sent as multipart/form-data. Nothing is charged by Foan, but it creates real records on your Plivo account and cannot be undone through this API.
Scope: numbers:write
Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
data |
string | yes | A JSON string (not an object) describing the application. Keys: country_iso (required, two letters), number_type (default local), user_type (business default, or individual), alias (optional label, must not contain “[org:”), end_user {name, last_name, business_name, address_line1, address_line2, city, postal_code, country, registration_number, email}, documents (required, at least one) [{document_type_id, data_fields {field_name: value}}]. End-user address, name, registration number and email are used as defaults for each document’s fields. |
documents[0].file |
file | no | File for documents[0]. Add documents[1].file, documents[2].file and so on for the other documents. Optional per document; up to 10 files, 10 MB each. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/compliance/applications" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 201
{ "success": true, "data": { "compliance": { "compliance_id": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809", "alias": "Priya Sharma Foods IN compliance [org:cmf3k2j9x0001abcd1234efgh]", "status": "submitted", "country_iso": "IN", "number_type": "local", "user_type": "business", "rejection_reason": null, "created_at": "2026-09-10T09:00:00Z", "documents": [ { "document_id": "d0c1e2f3-a4b5-4c6d-8e7f-9a0b1c2d3e4f", "document_type_id": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d", "status": "accepted" } ] } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, data is not valid JSON, country_iso or documents missing, alias contains “[org:”, no Plivo requirement found for the combination, or stored Plivo credentials incomplete. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | No Plivo account is connected. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | Plivo did not return an ID for the end user, a document or the application. |
Refresh a Plivo compliance application
Section titled “Refresh a Plivo compliance application”POST /telephony/plivo/compliance/applications/{complianceId}
Asks Plivo for the current status of one of your compliance applications, records the answer, and returns it with the normalised KYC state and next action. The application must be linked to your organisation (403 otherwise).
Scope: numbers:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
complianceId |
path | string | yes | Plivo compliance application ID. |
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/compliance/applications/{complianceId}" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "compliance": { "compliance_id": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809", "alias": "Priya Sharma Foods IN compliance [org:cmf3k2j9x0001abcd1234efgh]", "status": "accepted", "country_iso": "IN", "number_type": "local", "user_type": "business", "rejection_reason": null, "created_at": "2026-09-10T09:00:00Z", "documents": [ { "document_id": "d0c1e2f3-a4b5-4c6d-8e7f-9a0b1c2d3e4f", "document_type_id": "7a6b5c4d-3e2f-4a1b-9c8d-7e6f5a4b3c2d", "status": "accepted" } ] }, "kyc": { "state": "approved", "providerStatus": "accepted", "submittedAt": "2026-09-10T09:00:00.000Z", "decidedAt": "2026-09-12T14:20:00.000Z", "rejectionReason": null, "lastCheckedAt": "2026-09-24T08:30:00.000Z", "nextAction": { "code": "buy_number", "message": "KYC is approved. You can buy a number." } } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or stored Plivo credentials incomplete. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or the application is not linked to your organisation. |
| 404 | No Plivo account connected, or Plivo says the application does not exist. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Link a compliance application to a number
Section titled “Link a compliance application to a number”POST /telephony/plivo/compliance/link
Attaches a Plivo compliance application to one of your Plivo numbers (at Plivo and in Foan) and adds it to your organisation’s approved list. Identify the number by numberId or phoneNumber. The application must be one Foan created for your organisation or already linked to it; applications belonging to another organisation are refused.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
complianceApplicationId |
string | yes | Plivo compliance application ID. |
numberId |
string | no | Foan phone number ID. Takes precedence over phoneNumber. |
phoneNumber |
string | no | E.164 number, exactly as stored in Foan, used when numberId is not given. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/compliance/link" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","complianceApplicationId":"a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809","numberId":"cmf3k2j9x0002abcd1234efgh"}'Response 200
{ "success": true, "data": { "linked": true, "numberId": "cmf3k2j9x0002abcd1234efgh", "complianceApplicationId": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, stored Plivo credentials incomplete, or Plivo rejected the update. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or the application belongs to or was not created for this organisation. |
| 404 | No Plivo account connected, number not found in your organisation, or Plivo has no such application. |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | Could not verify the application with Plivo. |
Get number KYC status
Section titled “Get number KYC status”GET /telephony/kyc/{organisationId}
Where your number KYC stands, one entry per provider application (compliance applications for your connected number provider, and the India numbering wizard), each with its state, whether it currently allows a purchase, and a plain next-action message. Answers from Foan’s records without calling any provider; use POST /telephony/kyc/refresh to re-check with providers.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/kyc/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "organisationId": "cmf3k2j9x0001abcd1234efgh", "applications": [ { "provider": "plivo", "providerApplicationId": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809", "state": "approved", "providerStatus": "accepted", "statusSource": "provider_api", "lastStep": null, "submittedAt": "2026-09-10T09:00:00.000Z", "decidedAt": "2026-09-12T14:20:00.000Z", "rejectionReason": null, "lastCheckedAt": "2026-09-24T08:30:00.000Z", "countryIso": "IN", "numberType": "local", "stale": false, "verificationUnavailable": false, "statusUnrecognised": false, "purchaseAllowed": true, "purchaseBlockedReason": null, "nextAction": { "code": "buy_number", "message": "KYC is approved. You can buy a number." } } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Organisation not found. |
| 429 | Rate limited |
Refresh number KYC status
Section titled “Refresh number KYC status”POST /telephony/kyc/refresh
Asks each provider for the latest status of your KYC applications, records the answers, and returns the refreshed view (same shape as GET /telephony/kyc/{organisationId}). A provider that cannot be reached is reported per application as verificationUnavailable, never as a rejection.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/kyc/refresh" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "organisationId": "cmf3k2j9x0001abcd1234efgh", "applications": [ { "provider": "plivo", "providerApplicationId": "a1b2c3d4-5e6f-7081-92a3-b4c5d6e7f809", "state": "approved", "providerStatus": "accepted", "statusSource": "provider_api", "lastStep": null, "submittedAt": "2026-09-10T09:00:00.000Z", "decidedAt": "2026-09-12T14:20:00.000Z", "rejectionReason": null, "lastCheckedAt": "2026-09-24T08:30:00.000Z", "countryIso": "IN", "numberType": "local", "stale": false, "verificationUnavailable": false, "statusUnrecognised": false, "purchaseAllowed": true, "purchaseBlockedReason": null, "nextAction": { "code": "buy_number", "message": "KYC is approved. You can buy a number." } } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Organisation not found. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
Assign a number to an agent
Section titled “Assign a number to an agent”POST /telephony/numbers/{id}/assign
Sets which agent answers inbound calls on this number, or unassigns it when agentId is null or omitted. An agent answers on only one number, so any other number that agent had is unassigned. The agent’s calling number and provider are also set to this number. For Twilio numbers, the number’s webhook at Twilio is repointed to the agent. For a Plivo number imported with routingStatus NEEDS_SETUP (it answered through another Plivo application), assigning an agent points the number at Foan’s application on your Plivo account first. For a number on a connected Plivo, Twilio or Exotel account the change runs under that account’s lock, so it can be refused with 409 while a connect, sync or disconnect of the account is running.
Scope: numbers:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string | yes | Foan phone number ID (from the numbers list). |
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
agentId |
string | no | Agent to assign. null or omitted unassigns the number. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/numbers/{id}/assign" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","agentId":"cmf3k2j9x0003abcd1234efgh"}'Response 200
{ "success": true, "data": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "+14155550123", "status": "ACTIVE", "routingStatus": "ASSIGNED", "capabilities": { "voice": true, "sms": false }, "agentId": "cmf3k2j9x0003abcd1234efgh", "agent": { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, "assignedAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" } ], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_purchase", "type": "local", "region": "California" }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or the carrier (Twilio or Plivo) rejected the routing update or the stored credentials. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Number not found in your organisation, or agent not found. |
| 409 | The Plivo account must be reconnected before this number can be assigned (“Reconnect the Plivo account before assigning this number”), or another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Set outbound agents for a number
Section titled “Set outbound agents for a number”POST /telephony/numbers/{id}/outbound-agents
Replaces the full list of agents that dial out using this number as caller ID. Many agents can share one outbound number. Agents currently using this number but left out of agentIds lose their caller ID (their calling number is cleared), so send the complete list. An empty list clears everyone. For a number on a connected Plivo, Twilio or Exotel account the change runs under that account’s lock.
Scope: numbers:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string | yes | Foan phone number ID. |
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
agentIds |
array of string | yes | Every agent that should dial out from this number. Duplicates are ignored. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/numbers/{id}/outbound-agents" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","agentIds":["cmf3k2j9x0003abcd1234efgh","cmf3k2j9x0006abcd1234efgh"]}'Response 200
{ "success": true, "data": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "plivo", "phoneNumber": "+14155550123", "countryCode": "US", "providerNumberId": "14155550123", "friendlyName": "+14155550123", "status": "ACTIVE", "routingStatus": "ASSIGNED", "capabilities": { "voice": true, "sms": false }, "agentId": "cmf3k2j9x0003abcd1234efgh", "agent": { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, "assignedAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" } ], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": { "source": "plivo_purchase", "type": "local", "region": "California" }, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "outboundAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, { "id": "cmf3k2j9x0006abcd1234efgh", "name": "Payment reminder" } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (agentIds must be an array of strings). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Number not found in your organisation, or one or more agents not found. |
| 409 | Another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Replace an outbound number
Section titled “Replace an outbound number”POST /telephony/numbers/replace
Moves every agent that dials out from one number onto another number in one step (for example to rotate away from a number flagged as spam). Only outbound caller ID moves; inbound assignment is unchanged. Both numbers must be in your organisation and the target must be active.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
fromNumberId |
string | yes | Number the agents currently dial out from. |
toNumberId |
string | yes | Number they should dial out from instead. Must be active. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/numbers/replace" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","fromNumberId":"cmf3k2j9x0002abcd1234efgh","toNumberId":"cmf3k2j9x0005abcd1234efgh"}'Response 200
{ "success": true, "data": { "movedAgentCount": 2, "movedAgentIds": [ "cmf3k2j9x0003abcd1234efgh", "cmf3k2j9x0006abcd1234efgh" ], "from": { "id": "cmf3k2j9x0002abcd1234efgh", "phoneNumber": "+918041234567" }, "to": { "id": "cmf3k2j9x0005abcd1234efgh", "phoneNumber": "+918041234568" } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or fromNumberId equals toNumberId. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Either number not found in your organisation (or the target is inactive). |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Create India telephony profile
Section titled “Create India telephony profile”POST /telephony/voicelink/client
Creates your organisation’s profile with Foan’s India numbering partner (VoiceLink), the first step before India KYC and buying Indian numbers. Uses your organisation’s name, the API key owner’s email and the organisation’s mobile. Nothing is charged. Safe to repeat: if a profile exists it is returned unchanged.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/voicelink/client" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 201
{ "success": true, "data": { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "voicelink", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "config": { "kyc_status": "not_started", "vl_username": "configured", "vl_password": "configured" } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or India number provisioning is not enabled. |
| 404 | Organisation not found. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | The partner did not return a client ID. |
Get India KYC status
Section titled “Get India KYC status”GET /telephony/voicelink/status/{organisationId}
Asks Foan’s India numbering partner (VoiceLink) for your India KYC status right now, records the answer, and returns your India connection, the partner’s verdict reduced to state, wording and rejection reason, and your full KYC view.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/voicelink/status/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "integration": { "id": "cmf3k2j9x0004abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "provider": "voicelink", "isActive": true, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z", "config": { "kyc_status": "in_progress", "kyc_step": "pan", "last_kyc_step": "pan", "vl_username": "configured", "vl_password": "configured" } }, "remoteStatus": { "state": "in_progress", "providerStatus": "pending", "rejectionReason": null }, "kyc": { "organisationId": "cmf3k2j9x0001abcd1234efgh", "applications": [ { "provider": "voicelink", "providerApplicationId": "4821", "state": "in_progress", "providerStatus": "pending", "statusSource": "provider_api", "lastStep": "pan", "submittedAt": null, "decidedAt": null, "rejectionReason": null, "lastCheckedAt": "2026-09-24T08:30:00.000Z", "countryIso": null, "numberType": null, "stale": false, "verificationUnavailable": false, "statusUnrecognised": false, "purchaseAllowed": false, "purchaseBlockedReason": "kyc_not_approved", "nextAction": { "code": "continue_kyc", "message": "Finish the remaining KYC steps." } } ] } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | No India profile exists yet (create one with POST /telephony/voicelink/client). |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
Submit an India KYC step
Section titled “Submit an India KYC step”POST /telephony/voicelink/kyc/{step}
Submits one step of the India number KYC wizard to Foan’s India numbering partner (VoiceLink), in order: register, pan, aadhaar, gst (business accounts only), final. The register step records the customer’s legal acceptance of the partner’s terms, so send it only after the customer has agreed. The aadhaar step returns aadhaarVerificationUrl, a DigiLocker link the customer must open in a browser before gst or final. Submits immediately; there is no confirm flag.
Scope: numbers:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
step |
path | string: register, pan, aadhaar, gst, final | yes | Which wizard step to submit. |
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
payload |
object | no | Step fields, passed to the partner as-is. register: account_type (individual or business), full_name, email, phone, billing_address, term_and_condition, business_name (businesses). pan: pan_holder_name, pan_number. aadhaar: optional redirect_url. gst: gst_number (15-character GSTIN). final: nothing. Defaults to {}. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/voicelink/kyc/{step}" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","payload":{"pan_holder_name":"Priya Sharma","pan_number":"YOUR_PAN_NUMBER"}}'Response 200
{ "success": true, "data": { "provider": { "state": null, "providerStatus": null, "rejectionReason": null }, "kyc": { "state": "in_progress", "lastStep": "pan", "nextAction": { "code": "continue_kyc", "message": "Finish the remaining KYC steps." } } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (payload must be an object), invalid step, no India profile yet, or the partner rejected the submitted details. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or India number provisioning is not enabled. |
| 404 | No India connection exists. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | The partner rejected Foan’s reseller credentials. |
List India numbers to buy
Section titled “List India numbers to buy”GET /telephony/voicelink/dids/{organisationId}
Lists Indian numbers available to buy from Foan’s India numbering partner (VoiceLink), with each number’s monthly price. Paginated. Read-only.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
countryCode |
query | string | no | Dialling code. country_code is accepted as an alias. |
country_code |
query | string | no | Alias of countryCode. |
type |
query | integer | no | Partner’s number type code. |
number |
query | string | no | Digits to search for. |
months |
query | integer | no | Rental term the prices are quoted for. |
page |
query | integer | no | |
perPage |
query | integer | no | per_page is accepted as an alias. |
per_page |
query | integer | no | Alias of perPage. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/voicelink/dids/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "dids": [ { "id": 90211, "did_number": "918041234567", "country_code": "91", "type": 2, "months": 1, "city": "Bengaluru", "state": "Karnataka", "rate_per_month": 499, "currency": "INR" } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or India number provisioning is not enabled. |
| 404 | Organisation not found. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | The partner rejected Foan’s reseller credentials. |
Buy an India number
Section titled “Buy an India number”POST /telephony/voicelink/purchase
Buys an Indian number immediately from Foan’s India numbering partner (VoiceLink), maps it to your India profile and routes its calls to Foan. There is no confirm step or dry run. Needs India KYC approved (checked live with the partner) and a wallet funded above the minimum balance. Buying with an API key never tops up the partner float: if the float is low you get 402 and an owner or admin must complete one purchase from the dashboard.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
didId |
integer | yes | The number’s id from the India numbers list. |
didNumber |
string | yes | The number itself from the list, for example 918041234567. |
months |
integer | no | Rental term in months. Defaults to 1. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/voicelink/purchase" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","didId":90211,"didNumber":"918041234567","months":1}'Response 201
{ "success": true, "data": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "voicelink", "phoneNumber": "+918041234567", "countryCode": "IN", "providerNumberId": "90211", "friendlyName": "918041234567", "status": "ACTIVE", "routingStatus": "READY", "capabilities": { "voice": true, "sms": false }, "agentId": null, "agent": null, "assignedAgents": [], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": {}, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, or no India profile exists yet. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded or below the minimum balance, or the India float is too low and cannot be topped up with an API key. |
| 403 | Missing scope, foreign organisation, or India number provisioning is not enabled. |
| 404 | No India connection exists. |
| 409 | India KYC is not approved; the message says what to do next. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | The partner did not return an order or bot ID, or rejected Foan’s reseller credentials. |
| 503 | KYC could not be confirmed with the partner just now; nothing was bought. |
Renew an India number
Section titled “Renew an India number”POST /telephony/voicelink/numbers/{id}/renew
Renews the rental on one of your Indian numbers with Foan’s India numbering partner (VoiceLink), immediately and with no confirm step. Needs a wallet funded above the minimum balance. The renewal is paid from Foan’s reseller account with the partner, not debited from your wallet by this call.
Scope: numbers:write
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | string | yes | Foan phone number ID of an active India number. |
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/voicelink/numbers/{id}/renew" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "number": { "id": "cmf3k2j9x0002abcd1234efgh", "organisationId": "cmf3k2j9x0001abcd1234efgh", "integrationId": "cmf3k2j9x0004abcd1234efgh", "provider": "voicelink", "phoneNumber": "+918041234567", "countryCode": "IN", "providerNumberId": "90211", "friendlyName": "918041234567", "status": "ACTIVE", "routingStatus": "ASSIGNED", "capabilities": { "voice": true, "sms": false }, "agentId": "cmf3k2j9x0003abcd1234efgh", "agent": { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" }, "assignedAgents": [ { "id": "cmf3k2j9x0003abcd1234efgh", "name": "Order confirmation" } ], "customerRatePerMin": null, "monthlyCostUsd": 0, "metadata": {}, "createdAt": "2026-09-20T10:15:00.000Z", "updatedAt": "2026-09-24T08:30:00.000Z" }, "renewal": { "renewedAt": "2026-09-24T08:30:00.000Z", "purchaseId": "77120" } }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Wallet not funded or below the minimum balance. |
| 403 | Missing scope, foreign organisation, or India number provisioning is not enabled. |
| 404 | India number not found in your organisation, or the partner has no active renewal record for it. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 502 | The partner rejected Foan’s reseller credentials. |
Sync Plivo numbers
Section titled “Sync Plivo numbers”POST /telephony/plivo/sync
Re-reads every number on your connected Plivo account and reconciles Foan with it: new numbers are imported (for example ones you bought in the Plivo console), existing ones refreshed, and numbers no longer on the account are deactivated. Foan makes sure its application exists on your account and points imported numbers at it; a number that answers through another Plivo application is left alone with routingStatus NEEDS_SETUP. Numbers on Plivo subaccounts are never imported. Nothing is bought and no wallet funding is needed. One connect, sync or disconnect per carrier account runs at a time. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/plivo/sync" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "remoteCount": 5, "imported": 1, "updated": 4, "needsSetup": 1, "skippedOtherProvider": 0, "deactivated": 0, "syncedAt": "2026-10-05T09:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, the stored Plivo credentials are incomplete, or Plivo rejected them. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Plivo self-serve is not enabled. |
| 404 | No Plivo account is connected (“plivo integration not found”). |
| 409 | This organisation gets its Plivo numbers from Foan (Numbers from Foan), so it has no Plivo account of its own to use here. Use the /telephony/managed routes instead. Also returned when another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Sync Twilio numbers
Section titled “Sync Twilio numbers”POST /telephony/twilio/sync
Re-reads every incoming number on your connected Twilio account (all pages) and reconciles Foan with it: new numbers are imported, existing ones refreshed, and numbers no longer on the account are deactivated (only when the whole account was read). Nothing is changed at Twilio; a number’s voice webhook is set when you assign it to an agent. Nothing is bought and no wallet funding is needed. One connect, sync or disconnect per carrier account runs at a time. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/twilio/sync" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh"}'Response 200
{ "success": true, "data": { "remoteCount": 3, "imported": 1, "updated": 2, "needsSetup": 0, "skippedOtherProvider": 0, "deactivated": 0, "syncedAt": "2026-10-05T09:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, the stored Twilio credentials are incomplete, or Twilio rejected them. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Twilio self-serve is not enabled. |
| 404 | No Twilio account is connected (“twilio integration not found”). |
| 409 | Another connect, sync or disconnect of this carrier account is still running (“Another change to this carrier account is still running. Try again in a moment.”), or this change ran too long and was stopped. Retry shortly. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
| 503 | Foan could not take the lock it uses to serialise changes to one carrier account. Nothing was changed; retry shortly. |
Get Numbers from Foan status
Section titled “Get Numbers from Foan status”GET /telephony/managed/{organisationId}
Numbers from Foan: for an organisation billed in India with no carrier account of its own, Foan rents Indian numbers on its own Plivo account, under a compliance application and subaccount of the organisation’s own. Returns whether this is open to your organisation and how it buys numbers, the state of its business verification, its Foan phone account, prices, and the numbers it rents. Read-only and free; it may ask Plivo for a pending verification decision. Prices come from the rate card in minor units (paise for INR): today a number is Rs 500 a month (numberMonthlyMinor 50000) plus a phone line charge of Rs 0.50 a minute in 30-second blocks on its calls. A Muncho venue whose calls are paid from the Muncho wallet pays its numbers from that wallet instead, at Muncho’s prices.
Scope: numbers:read
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
organisationId |
path | string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
Example request
curl -X GET "https://api.foan.ai/api/v1/telephony/managed/{organisationId}" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 200
{ "success": true, "data": { "available": true, "route": "subaccount", "planRequired": false, "outsideIndia": false, "country": "IN", "useCases": [ { "id": "TRANSACTIONAL", "offered": true, "label": "Service and transactional", "example": "Confirming a booking, a delivery slot or an appointment; answering customers who call in." }, { "id": "PROMOTIONAL", "offered": false, "label": "Promotional", "example": "Offering a discount or a new product to people who did not ask to be called." }, { "id": "FINANCIAL", "offered": false, "label": "Financial services (licensed entities)", "example": "Calls from a bank, lender or insurer about their regulated products." } ], "onboarding": { "state": "accepted", "useCase": "TRANSACTIONAL", "businessName": "Example Foods Private Limited", "registrationKind": "COI", "taxKind": "PAN", "details": null, "rejectionReason": null, "submittedAt": "2026-10-01T06:00:00.000Z", "checkedAt": "2026-10-02T04:10:00.000Z", "canBuy": true, "documents": [ { "role": "REGISTRATION", "kind": "COI", "fileName": "incorporation.pdf", "sizeBytes": 182344, "uploadedAt": "2026-10-01T06:00:00.000Z" }, { "role": "TAX", "kind": "PAN", "fileName": "pan.pdf", "sizeBytes": 96210, "uploadedAt": "2026-10-01T06:00:00.000Z" } ] }, "account": "ready", "prices": { "currency": "INR", "numberMonthlyMinor": 50000, "numberMonthly": "Rs 500", "phoneLinePerMinuteMinor": 50, "phoneLinePerMinute": "Rs 0.50", "phoneLineBlockSeconds": 30 }, "graceDays": 7, "numbers": [ { "id": "cmf3k2j9x0009abcd1234efgh", "phoneNumber": "+918041234567", "country": "IN", "status": "active", "routingStatus": "PURCHASED", "agentId": null, "monthlyPrice": "Rs 500", "paidThrough": "2026-11-05T09:30:00.000Z", "releaseOn": null, "since": "2026-10-05T09:30:00.000Z" } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | The key lacks the required scope, or organisationId is not the key’s own organisation. |
| 404 | Organisation not found. |
| 429 | Rate limited |
Submit business verification for Numbers from Foan
Section titled “Submit business verification for Numbers from Foan”POST /telephony/managed/onboarding
Numbers from Foan: for an organisation billed in India with no carrier account of its own, Foan rents Indian numbers on its own Plivo account, under a compliance application and subaccount of the organisation’s own. Files (or, after a rejection, refiles) your business verification with Plivo: a compliance application in your organisation’s name built from your details and two documents, a registration certificate (certificate of incorporation or Udyam) and a PAN or GST certificate. Both documents are required the first time; a refile may replace either and reuses the stored copy of the other. Plivo usually decides within a day; read the decision with Get Numbers from Foan status. After submission Foan also sets up your Foan phone account (retried later if it fails). Nothing is charged. Refused while verification is already submitted or accepted, and when your organisation has its own Plivo account connected. Sent as multipart/form-data. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
data |
string | yes | JSON string with the business details, see the example. |
registration |
file | no | Registration certificate: PDF, JPEG or PNG, at most 5 MB. |
tax |
file | no | PAN or GST certificate: PDF, JPEG or PNG, at most 5 MB. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/managed/onboarding" \ -H "Authorization: Bearer $FOAN_API_KEY"Response 201
{ "success": true, "data": { "available": true, "route": "subaccount", "planRequired": false, "outsideIndia": false, "country": "IN", "useCases": [ { "id": "TRANSACTIONAL", "offered": true, "label": "Service and transactional", "example": "Confirming a booking, a delivery slot or an appointment; answering customers who call in." }, { "id": "PROMOTIONAL", "offered": false, "label": "Promotional", "example": "Offering a discount or a new product to people who did not ask to be called." }, { "id": "FINANCIAL", "offered": false, "label": "Financial services (licensed entities)", "example": "Calls from a bank, lender or insurer about their regulated products." } ], "onboarding": { "state": "submitted", "useCase": "TRANSACTIONAL", "businessName": "Example Foods Private Limited", "registrationKind": "COI", "taxKind": "PAN", "details": { "businessName": "Example Foods Private Limited", "registrationKind": "COI", "registrationNumber": "U12345KA2020PTC123456", "taxKind": "PAN", "taxNumber": "ABCDE1234F", "email": "compliance@example.com", "addressLine1": "12 Example Road", "addressLine2": "", "city": "Bengaluru", "state": "Karnataka", "postalCode": "560001" }, "rejectionReason": null, "submittedAt": "2026-10-05T09:30:00.000Z", "checkedAt": "2026-10-05T09:30:00.000Z", "canBuy": false, "documents": [ { "role": "REGISTRATION", "kind": "COI", "fileName": "incorporation.pdf", "sizeBytes": 182344, "uploadedAt": "2026-10-01T06:00:00.000Z" }, { "role": "TAX", "kind": "PAN", "fileName": "pan.pdf", "sizeBytes": 96210, "uploadedAt": "2026-10-01T06:00:00.000Z" } ] }, "account": "ready", "prices": { "currency": "INR", "numberMonthlyMinor": 50000, "numberMonthly": "Rs 500", "phoneLinePerMinuteMinor": 50, "phoneLinePerMinute": "Rs 0.50", "phoneLineBlockSeconds": 30 }, "graceDays": 7, "numbers": [] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed, data is not a JSON string, a required detail is missing or invalid (“Please add …”), a document is not a real PDF, JPEG or PNG, a document is missing, or Plivo refused the details. |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Numbers from Foan is not open to this organisation yet. |
| 404 | Organisation not found. |
| 409 | Not possible right now: no Foan plan, not billed in India, own Plivo account connected, use case not offered, verification already submitted or approved, or another submission is in progress. The message says which. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
| 500 | A document over 5 MB, or a file in a field other than registration or tax, is currently rejected with a generic server error. |
| 502 | Plivo did not confirm the submission. Retry. |
| 503 | Numbers from Foan is not configured, Plivo could not be reached, Plivo’s document requirements no longer match what Foan collects, or a Muncho venue’s billing is moving wallets. Nothing was filed; retry later. |
Search Numbers from Foan
Section titled “Search Numbers from Foan”POST /telephony/managed/search
Numbers from Foan: for an organisation billed in India with no carrier account of its own, Foan rents Indian numbers on its own Plivo account, under a compliance application and subaccount of the organisation’s own. Lists up to 20 Indian numbers you may rent, with Foan’s monthly price from the rate card. Only series your use case allows come back (today Bengaluru 080 and Mumbai 022 landlines for service and transactional calls). Searching reserves nothing and charges nothing. Needs a submitted business verification; canBuy says whether it is approved yet. Prices come from the rate card in minor units (paise for INR): today a number is Rs 500 a month (numberMonthlyMinor 50000) plus a phone line charge of Rs 0.50 a minute in 30-second blocks on its calls. A Muncho venue whose calls are paid from the Muncho wallet pays its numbers from that wallet instead, at Muncho’s prices.
Scope: numbers:read
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
pattern |
string | no | Digits the number should contain; non-digits are removed. |
city |
string | no | Keep only numbers in this city (Bengaluru or Mumbai), case-insensitive. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/managed/search" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","pattern":"8041","city":"Bengaluru"}'Response 200
{ "success": true, "data": { "country": "IN", "canBuy": true, "prices": { "currency": "INR", "numberMonthlyMinor": 50000, "numberMonthly": "Rs 500", "phoneLinePerMinuteMinor": 50, "phoneLinePerMinute": "Rs 0.50", "phoneLineBlockSeconds": 30 }, "numbers": [ { "phoneNumber": "+918041234567", "series": 80, "city": "Bengaluru", "type": "local", "monthlyPrice": "Rs 500", "monthlyPriceMinor": 50000, "currency": "INR" } ] }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or Numbers from Foan is not open to this organisation yet. |
| 404 | Organisation not found. |
| 409 | No business verification submitted yet, no Foan plan, or not billed in India. |
| 429 | Rate limited: this route allows about 20 requests per minute per API key (body: {success: false, message}). |
| 500 | Plivo returned an error for the search. |
| 503 | Numbers from Foan is not configured, the Muncho wallet could not be reached, or a Muncho venue’s billing is moving wallets. |
Rent a number from Foan
Section titled “Rent a number from Foan”POST /telephony/managed/purchase
Numbers from Foan: for an organisation billed in India with no carrier account of its own, Foan rents Indian numbers on its own Plivo account, under a compliance application and subaccount of the organisation’s own. Rents the number immediately and charges its first month to your wallet up front (Foan wallet, or the Muncho wallet for a Muncho venue paying from it). There is no confirm step or dry run. Only the number comes from the request: the price is the rate card’s and the compliance application and subaccount are your organisation’s own. Your verification is re-checked live with Plivo first and must be accepted. Each month is then charged when the paid month runs out; an unpaid month is warned about, given graceDays of grace, then the number is released. If any step after the charge fails, the number is given back and the charge refunded. A number you already rent is returned as it is, with no new charge. Prices come from the rate card in minor units (paise for INR): today a number is Rs 500 a month (numberMonthlyMinor 50000) plus a phone line charge of Rs 0.50 a minute in 30-second blocks on its calls. A Muncho venue whose calls are paid from the Muncho wallet pays its numbers from that wallet instead, at Muncho’s prices. With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
phoneNumber |
string | yes | A number from Search Numbers from Foan (E.164 or digits). |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/managed/purchase" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","phoneNumber":"+918041234567"}'Response 201
{ "success": true, "data": { "id": "cmf3k2j9x0009abcd1234efgh", "phoneNumber": "+918041234567", "country": "IN", "status": "active", "routingStatus": "PURCHASED", "agentId": null, "monthlyPrice": "Rs 500", "paidThrough": "2026-11-05T09:30:00.000Z", "releaseOn": null, "since": "2026-10-05T09:30:00.000Z" }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 402 | Your wallet cannot pay the first month (“A number costs Rs 500 a month, paid from your wallet up front. Top up to continue.”), your plan is paused for non-payment, or the Muncho wallet refused. Nothing was charged. |
| 403 | Missing scope, foreign organisation, a dashboard user who is not an owner or admin, or Numbers from Foan is not open to this organisation yet. |
| 404 | Organisation not found. |
| 409 | Not possible: no verification submitted, verification not approved or suspended, a series your use case may not use (140, 160, or outside Bengaluru and Mumbai), the number is already connected or already being bought, the number was just taken (refunded), own Plivo account connected, no Foan plan, or not billed in India. The message says which. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
| 500 | Your Foan phone account needs attention; contact support. |
| 502 | The purchase is still finishing (“It will complete, or be refunded, automatically”), or set-up failed and the number was given back and the charge refunded. Check Get Numbers from Foan status before retrying. |
| 503 | Your verification could not be confirmed with Plivo just now, Numbers from Foan is not configured, or the Muncho wallet could not be reached or is moving. Nothing was bought. |
Release a number rented from Foan
Section titled “Release a number rented from Foan”POST /telephony/managed/release
Gives a number rented from Foan (Numbers from Foan) back to the carrier, permanently. Its monthly charges stop; the rest of the month already paid is not refunded. Refused with 409 while an agent answers on it or dials out from it, unless force is true, in which case those agents lose the number. Numbers on your own carrier account cannot be released here (release them in that carrier’s console). With an API key, the numbers:write scope is the permission (only an owner or admin can create keys); a dashboard session must belong to an owner or admin.
Scope: numbers:write
Body (application/json)
| Field | Type | Required | Description |
|---|---|---|---|
organisationId |
string | yes | Your organisation ID. Must be the API key’s own organisation, otherwise 403. |
numberId |
string | yes | Foan phone number ID from Get Numbers from Foan status. |
force |
boolean | no | Release even though agents still use the number; they lose it. |
Example request
curl -X POST "https://api.foan.ai/api/v1/telephony/managed/release" \ -H "Authorization: Bearer $FOAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"organisationId":"cmf3k2j9x0001abcd1234efgh","numberId":"cmf3k2j9x0009abcd1234efgh"}'Response 200
{ "success": true, "data": { "released": true, "numberId": "cmf3k2j9x0009abcd1234efgh", "phoneNumber": "+918041234567", "freedAgents": 0 }}Errors
| Code | When |
|---|---|
| 400 | Validation failed (the error message lists the failing fields). |
| 401 | Missing, invalid or revoked API key, or the key owner no longer belongs to the organisation. |
| 403 | Missing scope, foreign organisation, or a dashboard user who is not an owner or admin. |
| 404 | Not an active number rented from Foan in this organisation. |
| 409 | Agents still use this number (“1 agent(s) still use this number …”); move them first or send force: true. |
| 429 | Rate limited: about 20 requests per minute per API key, plus a stricter shared limit of about 30 per minute on this sensitive route (body: {success: false, message}). |
| 502 | The carrier could not release the number just now. It is still yours; retry shortly. |
| 503 | Numbers from Foan is not configured on this server. |

