Skip to main content

Dynamic SIP Trunking

Connect your SIP system to Exotel's PSTN infrastructure via REST API. Create trunks, whitelist IPs, map phone numbers, and configure routing โ€” all programmatically.


How it worksโ€‹

Outbound:  Your SIP system โ†’ Exotel SIP trunk โ†’ PSTN
Inbound: PSTN โ†’ Exotel โ†’ Flow (Connect applet) โ†’ SIP trunk โ†’ Your SIP system

Call directionsโ€‹

DirectionPathRequires
Outbound (SIP โ†’ PSTN)Your SIP โ†’ Exotel โ†’ PSTNWhitelisted IP (ACL) + phone number mapped to trunk
Inbound (PSTN โ†’ SIP)PSTN โ†’ Exotel Flow โ†’ SIP trunk โ†’ Your systemDestination URI + Flow with Connect applet using sip:<trunk_sid>
BidirectionalBoth aboveBoth above

Routing modesโ€‹

ModeUse when
pstnStandard PSTN inbound/outbound (default)
flowRoute calls through a Voice AI / StreamKit flow

Quick setupโ€‹

1. Create trunk           โ†’  get trunk_sid
2. Map phone number โ†’ get phone_number_id
3. Whitelist your IP โ†’ enables outbound
4. Set destination URI โ†’ enables inbound

API referenceโ€‹

Base URLs

RegionURL
Mumbaihttps://api.in.exotel.com
Singaporehttps://api.exotel.com

Auth: HTTP Basic โ€” API key as username, API token as password.

Rate limit: 200 requests/minute.

All endpoints are prefixed with /v2/accounts/{account_sid}.


Create trunkโ€‹

POST /v2/accounts/{account_sid}/trunks
ParameterRequiredDescription
trunk_nameYesAlphanumeric + underscores, max 16 chars
nso_codeYesUse "any any"
domain_nameYes{account_sid}.pstn.exotel.com
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks' \
-H 'Content-Type: application/json' \
-d '{
"trunk_name": "outbound_trunk",
"nso_code": "any any",
"domain_name": "<account_sid>.pstn.exotel.com"
}'
{
"trunk_sid": "TKxxxxxxxxxxxxxxxx",
"trunk_name": "outbound_trunk",
"status": "active",
"auth_type": "ip whitelist",
"domain_name": "<account_sid>.pstn.exotel.com",
"date_created": "2026-05-14T10:00:00Z"
}
note

Save trunk_sid โ€” required for all subsequent API calls.


Map phone numberโ€‹

POST /v2/accounts/{account_sid}/trunks/{trunk_sid}/phone_numbers
ParameterRequiredDescription
phone_numberYesE.164 format (+919876543210)
modeNopstn (default) or flow (Voice AI)
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/phone_numbers' \
-H 'Content-Type: application/json' \
-d '{
"phone_number": "+919876543210",
"mode": "pstn"
}'
{
"id": 41523,
"phone_number": "+919876543210",
"trunk_sid": "TKxxxxxxxxxxxxxxxx",
"mode": "pstn"
}
note

Save the numeric id โ€” needed for mode updates.


Whitelist IP (outbound)โ€‹

Registers your server's IP for outbound call authentication.

POST /v2/accounts/{account_sid}/trunks/{trunk_sid}/whitelisted_ips
ParameterRequiredDescription
ipYesYour server's public IPv4 address
maskNoCIDR subnet mask (default: 32)
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/whitelisted_ips' \
-H 'Content-Type: application/json' \
-d '{
"ip": "44.248.146.11",
"mask": 32
}'
{
"id": 123,
"ip": "44.248.146.11",
"mask": 32,
"trunk_sid": "TKxxxxxxxxxxxxxxxx"
}

Set destination URI (inbound)โ€‹

Configures where Exotel routes incoming calls.

POST /v2/accounts/{account_sid}/trunks/{trunk_sid}/destination_uris
ParameterRequiredDescription
destinations[].destinationYesSIP URI: <ip_or_fqdn>:<port>;transport=<tls|tcp>

Supported transports: tls (port 5061, recommended) ยท tcp (port 5060)

# IP-based
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/destination_uris' \
-H 'Content-Type: application/json' \
-d '{
"destinations": [
{ "destination": "44.248.146.11:5061;transport=tls" }
]
}'
# FQDN-based
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/destination_uris' \
-H 'Content-Type: application/json' \
-d '{
"destinations": [
{ "destination": "sip.yourcompany.com:5061;transport=tls" }
]
}'
{
"id": 456,
"destination": "sip:44.248.146.11:5061;transport=tls",
"type": "public",
"priority": 0,
"weight": 1,
"trunk_sid": "TKxxxxxxxxxxxxxxxx"
}

SIP digest credentialsโ€‹

Use instead of (or alongside) IP whitelisting for cloud platforms with dynamic IPs (e.g. ElevenLabs, LiveKit).

POST /v2/accounts/{account_sid}/trunks/{trunk_sid}/credentials
ParameterRequiredDescription
user_nameYesSIP digest username
passwordYesStrong password
friendly_nameNoLabel, max 32 chars
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/credentials' \
-H 'Content-Type: application/json' \
-d '{
"user_name": "voice_ai_user",
"password": "strong_password_here",
"friendly_name": "elevenlabs_prod"
}'

List credentials

curl 'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/credentials'

Delete credentials

curl -X DELETE \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/credentials?id=<credential_id>'

Update phone number modeโ€‹

Switch a mapped number between PSTN and Flow (Voice AI) routing.

PUT /v2/accounts/{account_sid}/trunks/{trunk_sid}/phone_numbers/{phone_number_id}
curl -X PUT \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/phone_numbers/41523' \
-H 'Content-Type: application/json' \
-d '{
"phone_number": "+919876543210",
"mode": "flow"
}'
note

Use the numeric id from the Map Phone Number response, not the phone number itself.


Set caller ID (trunk alias)โ€‹

Sets the number displayed to called parties on outbound calls.

POST /v2/accounts/{account_sid}/trunks/{trunk_sid}/settings
curl -X POST \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/settings' \
-H 'Content-Type: application/json' \
-d '{
"settings": [
{ "name": "trunk_external_alias", "value": "+919876543210" }
]
}'

Delete trunkโ€‹

DELETE /v2/accounts/{account_sid}/trunks?trunk_sid={trunk_sid}
curl -X DELETE \
'https://<api_key>:<api_token>@api.in.exotel.com/v2/accounts/<account_sid>/trunks?trunk_sid=<trunk_sid>'
warning

Permanently deletes the trunk and all associated phone numbers, ACLs, and destination URIs.


IP whitelist vs credentialsโ€‹

ScenarioMethod
On-premises PBX / SBC with fixed IPIP whitelist only
Cloud AI platforms (ElevenLabs, LiveKit)Credentials only
High-security productionBoth

Network & firewallโ€‹

For the full list of SIP signaling endpoints, inbound source IPs, media IP ranges, and firewall rules, refer to the official source of truth:

Network & Firewall Configuration โ†’

No audio?

If calls connect but have no audio or one-way audio โ€” UDP 10 000โ€“40 000 is likely blocked on your firewall.


SIP error codesโ€‹

4xx โ€” Client errorsโ€‹

CodeMeaningFix
400Bad requestReview SIP headers / SDP
401UnauthorizedCheck digest credentials
403ForbiddenIP not whitelisted โ€” check ACL
404Not foundVerify phone number mapping
408Request timeoutCheck firewall / reachability
415Unsupported mediaEnable G.711 A-Law; disable other codecs
480Temporarily unavailableCheck PBX/SBC availability
484Address incompleteUse E.164 format for numbers
486BusyDestination busy โ€” normal
488Not acceptableSDP/codec mismatch โ€” enable G.711 A-Law only

5xx โ€” Server errorsโ€‹

CodeMeaningFix
500Server errorRetry
502Bad gatewayUpstream routing issue โ€” retry
503Service unavailableCheck network / routing
504Gateway timeoutCheck firewall latency

Audio issues (no SIP error)โ€‹

SymptomCauseFix
No audio both waysRTP blockedOpen UDP 10 000โ€“40 000
One-way audioNAT / firewallCheck public IP in SDP
Audio drops mid-callRTP idle timeoutAdjust firewall timeout

API error codesโ€‹

CodeHTTPMeaningFix
1000404Resource not foundVerify trunk SID / phone number ID
1001400Mandatory parameter missingCheck all required fields
1002400Invalid parameterValidate formats (E.164, IP, etc.)
1007400Malformed JSONFix JSON syntax
1008409Duplicate resourceAlready configured โ€” safe to ignore
1011415Wrong content typeAdd Content-Type: application/json