SIP Trunking
Preview
SIP

Trunk Configuration

Deep configuration for enterprise SIP trunks: edge endpoints, authentication, codecs, capacity, failover, security hardening, and production-ready PBX snippets.

UDP · TCP · TLS

Signal on 5060 (UDP/TCP) or 5061 (TLS). Production should default to TLS.

Codec control

Allowlist G.711, G.729, and Opus; order sets SDP preference.

Digest + IP ACL

Layer credentials with CIDR allowlists to stop credential stuffing.

HA failover

Primary and secondary origination URIs with configurable hunt timeout.

Host

sip…

sip.sendexa.co

UDP/TCP

5060

Standard SIP

TLS

5061

Recommended

Realm

sendexa

sendexa.co

SIP edge reference

ParameterValueGuidance
SIP hostsip.sendexa.coUse the hostname (not a pinned IP) for multi-region HA
Port UDP5060Common for legacy PBXes; less preferred on public internet
Port TCP5060Better through strict NAT/firewall than UDP
Port TLS5061Encrypts signaling; required for SRTP-only policies
Supported methodsINVITE, ACK, BYE, CANCEL, OPTIONS, REGISTER, UPDATE, PRACK, REFER**REFER / transfers on selected plans
Session timersRFC 4028Default session-expires ~1800s; refresh with UPDATE/re-INVITE
OPTIONS keepaliveSupportedResponds to OPTIONS for trunk health probes

Authentication modes

Every INVITE and REGISTER toward Sendexa must authenticate. You can combine methods for defence in depth.

SIP digest (required)

Standard HTTP Digest authentication (RFC 3261 / RFC 2617 style) with realm sendexa.co.

  • Username and password set at POST /v1/sip/trunks
  • Challenge returned as SIP 401 with WWW-Authenticate
  • PBX must retry INVITE with Authorization header
  • Password rotatable without changing trunkId
IP ACL (recommended)

When ipAllowlist is non-empty, signaling from other source addresses is rejected with 403 before digest is even evaluated (or after failed match — treat ACL as hard gate).

  • CIDR notation: /32 for single host, /24 for subnet
  • Include all SBC egress IPs and HA peers
  • Update ACL before changing datacentre IPs
  • Empty list = any IP may attempt digest (weaker)

Codecs & media

CodecSDP nameBitrateUse case
G.711 µ-lawPCMU / ulaw~64 kbpsDefault PSTN interoperability (Americas)
G.711 A-lawPCMA / alaw~64 kbpsDefault PSTN interoperability (EU/Africa)
G.729G729~8 kbpsBandwidth-constrained WAN / many concurrent calls
OpusopusvariableWideband quality where both legs support it

Codec lists on the trunk act as an allowlist. Negotiation follows SDP offer/answer: if there is no intersection with the far end, Sendexa returns 488 Not Acceptable Here. Prefer listing PCMU and PCMA first for maximum PSTN compatibility, then G.729 / Opus.

Media encryption (SRTP)

When using TLS signaling, you may enable SDES-SRTP on your PBX. Sendexa supports optional SRTP on eligible trunks. If your endpoint requires encryption but the PSTN leg cannot, media is decrypted at the Sendexa edge before carrier handoff.

DTMF: use RFC 2833 / telephone-event (payload type negotiated in SDP). In-band DTMF is unreliable with compressed codecs.

Capacity & concurrency

FieldTypeRequiredDescription
maxConcurrentCallsintegeroptionalHard cap of simultaneous sessions (inbound + outbound) on the trunk. Excess INVITEs receive 486 or 503.
activeCallsintegeroptionalRead-only gauge returned on GET trunk detail — current established + early dialogs.
cpsHintintegeroptionalSoft guidance for calls-per-second. Sustained bursts may be rate-shaped; contact support for high CPS.

Origination failover

For inbound (DID → your infrastructure), configure two destinations. Sendexa hunts primary first, then failover.

ConditionAction
Primary returns 18x then 200 OKCall stays on primary
Primary TCP/TLS connect failureImmediate try of failover URI
Primary timeout (no provisional)After timeoutSeconds → failover
Primary 480 / 503 / 500Failover URI attempted
Primary 486 BusyNo failover by default (true busy)
Both destinations failCaller hears network busy / 503
JSON
{
"origination": {
"primaryUri": "sip:inbound@pbx-a.example.com:5061;transport=tls",
"failoverUri": "sip:inbound@pbx-b.example.com:5061;transport=tls",
"timeoutSeconds": 20
}
}

Update a trunk via REST

POST
/v1/sip/trunks

Create uses POST /v1/sip/trunks. Listing uses GET /v1/sip/trunks. Partial updates (password rotate, ACL, codecs) use the trunk resource:

PATCH
/v1/sip/trunks/:trunkId
Bash
# Rotate password and tighten ACL
curl -X PATCH 'https://api.sendexa.co/v1/sip/trunks/TRK-8c1e2b9a-4d3f-41a0-9e7c-12ab34cd56ef' \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic YOUR_DASHBOARD_BASE64_TOKEN' \
-d '{
"auth": { "password": "New-Long-Secret-2026!" },
"ipAllowlist": ["203.0.113.10/32", "203.0.113.11/32"],
"codecs": ["PCMU", "PCMA", "G729"],
"maxConcurrentCalls": 50
}'

Production PBX / SBC examples

pjsip_sendexa.conf
; /etc/asterisk/pjsip_sendexa.conf
; Include from pjsip.conf: #include pjsip_sendexa.conf
[transport-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
cert_file=/etc/asterisk/keys/asterisk.crt
priv_key_file=/etc/asterisk/keys/asterisk.key
method=tlsv1_2
[sendexa-auth]
type=auth
auth_type=userpass
username=cc_trunk_prod
password=S3cure-Long-Secret!
realm=sendexa.co
[sendexa-aor]
type=aor
contact=sip:sip.sendexa.co:5061;transport=tls
qualify_frequency=30
qualify_timeout=3.0
max_contacts=1
[sendexa-endpoint]
type=endpoint
context=from-sendexa
disallow=all
allow=ulaw,alaw,g729,opus
outbound_auth=sendexa-auth
aors=sendexa-aor
from_user=cc_trunk_prod
from_domain=sendexa.co
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
ice_support=no
media_encryption=sdes
media_encryption_optimistic=yes
timers=yes
timers_min_se=90
timers_sess_expires=1800
dtmf_mode=rfc4733
transport=transport-tls
trust_id_inbound=yes
send_pai=yes
send_rpid=yes
[sendexa-identify]
type=identify
endpoint=sendexa-endpoint
match=sip.sendexa.co
[sendexa-reg]
type=registration
transport=transport-tls
outbound_auth=sendexa-auth
server_uri=sip:sip.sendexa.co:5061;transport=tls
client_uri=sip:cc_trunk_prod@sendexa.co
contact_user=cc_trunk_prod
retry_interval=60
forbidden_retry_interval=300
expiration=600
line=yes
endpoint=sendexa-endpoint
extensions_sendexa.conf
; Outbound: dial 9 + E.164 or national → Sendexa
[to-sendexa]
exten => _9.,1,NoOp(Outbound via Sendexa ${EXTEN:1})
same => n,Set(CALLERID(num)=+233302123456)
same => n,Dial(PJSIP/${FILTER(0123456789+,${EXTEN:1})}@sendexa-endpoint,60)
same => n,Hangup()
; Inbound from Sendexa DIDs
[from-sendexa]
exten => _X.,1,NoOp(Inbound DID ${EXTEN} from ${CALLERID(all)})
same => n,Goto(ivr-main,s,1)

Security hardening

Prefer TLS 5061
Encrypts credentials and headers in transit. Pair with certificate validation on the PBX.
Tight IP allowlists
Only SBC egress IPs. Avoid broad /16 ranges. Review quarterly.
Rotate passwords
Use PATCH on the trunk when staff leave or after suspected leak. Update PBX in the same change window.
Disable SIP ALG
Firewall SIP ALG commonly breaks Contact/Via and causes one-way audio or registration flaps.
Rate-limit at SBC
Reject toll-fraud dial patterns before they hit Sendexa (e.g. unexpected international prefixes).
Monitor 401/403 spikes
Sudden digest failures may indicate credential spray or stale password after rotation.

Troubleshooting

SymptomLikely causeFix
401 loopsWrong password/realm or not answering challengeVerify username, password, realm=sendexa.co
403 on every INVITESource IP not in ACLAdd egress public IP as /32; check NAT
488 codecOnly Opus offered but trunk allowlist is G.711-onlyAlign codecs array and PBX allow list
One-way audioNAT / direct mediadirect_media=no, rtp_symmetric, open RTP ports
Inbound never ringsWrong destination URI or firewallCheck DID primaryUri, OPTIONS/INVITE reachability
486 at peakmaxConcurrentCalls exhaustedRaise limit or add trunks; check stuck calls