Asterisk PJSIP: Provider Inbound Call (Latest Versions)
Updated guide for modern Asterisk (PJSIP era): provider inbound call with real configs, common mistakes, and troubleshooting steps.
Inbound calls from a SIP provider are where most real-world Asterisk systems fail. Internal extensions may work perfectly, but inbound calls can still fail due to:
- Wrong identify match (provider IP not matched)
- Wrong context (call never enters your dialplan)
- DID not matching any extension pattern
- NAT / firewall causing audio failure after answer
- Provider sending DID in different headers than expected
This guide shows a production-safe inbound call design for modern PJSIP trunks and how to debug every step.
Inbound Call Flow (Mental Model)
- Customer dials your public number (DID / toll-free)
- Provider sends a SIP INVITE to your Asterisk public IP
- Asterisk matches the INVITE to a PJSIP endpoint (usually via
identify) - That endpoint has a context → call enters dialplan
- Dialplan routes call to IVR / Queue / Ring group / Custom logic
- When answered, audio flows via RTP ports (10000–20000 by default)
If any one step breaks, inbound calls fail.
Step 1: Build a Correct PJSIP Inbound Trunk (Identify + Context)
The most important inbound concept is: inbound calls will not reach your dialplan unless Asterisk matches the provider to an endpoint.
Example PJSIP trunk objects (IP-authenticated style). If your provider is registration-based, you still often need identify for inbound matching.
; ----------------------------
; PJSIP trunk: provider inbound
; ----------------------------
[provider-aor]
type=aor
contact=sip:PROVIDER_SIGNALING_IP
max_contacts=1
qualify_frequency=60
[provider-endpoint]
type=endpoint
transport=transport-udp
; IMPORTANT: inbound calls go to this dialplan context
context=inbound-provider
disallow=all
allow=ulaw,alaw
aors=provider-aor
; NAT/media stability defaults
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
[provider-identify]
type=identify
endpoint=provider-endpoint
; MUST match all provider signaling IPs / ranges
match=PROVIDER_SIGNALING_IP
If your provider has multiple IPs, add multiple match lines:
[provider-identify]
type=identify
endpoint=provider-endpoint
match=1.2.3.4
match=5.6.7.8
; match=1.2.3.0/24 (if provider gives a range)
Step 2: Design an Inbound Context That Never Breaks
In production, you want inbound dialplan to:
- Log what DID arrived
- Handle unknown DIDs safely
- Route known DIDs to IVR/Queue cleanly
- Be readable and easy to debug
A strong pattern is: one inbound context → normalize DID → route via GotoIf / lookup table / subroutine.
Inbound Dialplan Example (Direct DID Matching)
[inbound-provider]
; Always start by printing useful info
exten => _X.,1,NoOp(INBOUND PROVIDER CALL: exten=${EXTEN} caller=${CALLERID(all)})
same => n,NoOp(ToHeader=${PJSIP_HEADER(read,To)})
same => n,NoOp(RURI user=${PJSIP_HEADER(read,To)})
; Example: toll-free DID routes to IVR
exten => 1800123456,1,NoOp(Route DID 1800123456 to main IVR)
same => n,Goto(ivr-main,s,1)
; Example: support DID routes to queue
exten => 1800654321,1,NoOp(Route DID 1800654321 to support queue)
same => n,Queue(support-queue,t,,,300)
same => n,Hangup()
; Catch-all: if DID doesn't match anything, go to fallback
exten => _X.,1,NoOp(Unknown DID received: ${EXTEN})
same => n,Goto(inbound-fallback,s,1)
[inbound-fallback]
exten => s,1,NoOp(Fallback inbound handling)
same => n,Playback(sorry-cant-complete-as-dialed)
same => n,Hangup()
This works only if the provider delivers the DID as the Request-URI user (EXTEN). Some providers do not. That’s why you must understand where the DID is located.
Where the DID Actually Comes From (Critical Reality)
Providers may send the dialed number in different places:
- Request-URI user → Asterisk sees it as
${EXTEN}(easy case) - To: header user
- P-Called-Party-ID header
- Diversion header (forwarded DIDs)
If you match DIDs using ${EXTEN} but the provider sends DID only in headers,
your dialplan will never match and calls go to fallback.
Extract DID from Headers (PJSIP_HEADER Examples)
Example: read the To header (often looks like <sip:1800123456@yourdomain>):
[inbound-provider]
exten => s,1,NoOp(INBOUND: caller=${CALLERID(num)})
; Read headers
same => n,Set(TO_HDR=${PJSIP_HEADER(read,To)})
same => n,NoOp(To=${TO_HDR})
; Very simple DID extraction approach (example-style)
; NOTE: parsing SIP headers can be messy; prefer provider consistency if possible.
; We'll attempt to extract digits from To header:
same => n,Set(DID_RAW=${TO_HDR})
same => n,Set(DID=${FILTER(0-9,${DID_RAW})})
same => n,NoOp(Extracted DID=${DID})
; Route using DID
same => n,GotoIf($["${DID}"="1800123456"]?route_ivr)
same => n,GotoIf($["${DID}"="1800654321"]?route_support)
same => n,Goto(inbound-fallback,s,1)
same => n(route_ivr),Goto(ivr-main,s,1)
same => n(route_support),Queue(support-queue,t,,,300)
same => n,Hangup()
This technique makes inbound routing resilient even when EXTEN is not the DID.
Step 3: Confirm the Call Enters the Right Context
If you suspect inbound calls are not reaching dialplan, verify:
- The trunk endpoint has
context=inbound-provider - The identify match is correct
- Asterisk is receiving the INVITE
Debug commands:
asterisk -rvvv
pjsip show endpoints
pjsip show endpoint provider-endpoint
Look specifically for:
- Context: inbound-provider
- Transport: correct UDP/TCP/TLS
Step 4: Debug Inbound INVITE Matching (Identify Issues)
Most inbound failures are because Asterisk cannot map the incoming INVITE to the correct endpoint. Symptoms:
- Asterisk replies 401/404 unexpectedly
- Inbound INVITE shows “No matching endpoint found”
- Calls never hit your dialplan
Turn on logger and watch the source IP:
pjsip set logger on
If provider INVITE comes from a different IP than you matched, fix identify:
[provider-identify]
type=identify
endpoint=provider-endpoint
match=THE_REAL_SOURCE_IP
Providers often have separate IPs for:
- Registration server
- Inbound signaling
- Outbound gateway
Step 5: Confirm RTP Ports and Audio (Post-Answer Problems)
Many teams think inbound is fixed once calls “connect”. But the most common real-world complaint is: call connects but audio is one-way or silent.
That is usually RTP/NAT/firewall.
Verify RTP in Asterisk:
rtp set debug on
Required firewall ports (typical defaults):
sudo ufw allow 5060/udp
sudo ufw allow 10000:20000/udp
If your RTP range is different, check:
core show settings
Prove Inbound Signaling With tcpdump (When Logs Are Not Enough)
See inbound SIP traffic live:
sudo tcpdump -i any -n udp port 5060
Capture SIP + RTP for a full inbound call:
sudo tcpdump -i any -s 0 -w inbound_full.pcap udp port 5060 or udp portrange 10000-20000
Open inbound_full.pcap in Wireshark:
- Confirm INVITE arrives
- Confirm 100/180/200 sequence
- Check SDP for RTP IP/port correctness
- Verify RTP flows both directions
Production Checklist (Inbound Calls That Never Fail)
- Provider signaling IPs accurately included in
identify - Endpoint has correct
context - Inbound dialplan includes safe fallback for unknown DIDs
- Firewall allows SIP + RTP ports
- NAT config correct (external_signaling_address/external_media_address if behind NAT)
- Debug workflow documented (logger + tcpdump + Wireshark)
Key Takeaway
Inbound provider calls succeed only when:
- Asterisk matches the INVITE to the correct endpoint (identify)
- The call enters the correct dialplan (context)
- The DID is routed correctly (EXTEN or header-based extraction)
- RTP ports are reachable (no one-way audio)
If you debug inbound calls layer-by-layer (match → context → DID → RTP), inbound issues become predictable and easy to solve.
Want to see API-driven CRM + Telecom workflows in action? Try the WhatsApp bot or explore the demos.
Comments (0)
Be the first to comment.