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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' 'https://api.in.exotel.com/v2/accounts/<account_sid>/trunks/<trunk_sid>/credentials'

Delete credentials

curl -u '<api_key>:<api_token>' -X DELETE \
'https://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 -u '<api_key>:<api_token>' -X PUT \
'https://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 -u '<api_key>:<api_token>' -X POST \
'https://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 -u '<api_key>:<api_token>' -X DELETE \
'https://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