VICIfast
All docs

VICIdial SIP carrier setup — PJSIP trunk config

Wire a SIP trunk into a self-administered VICIdial box — where carrier definitions live, a working PJSIP endpoint and dialplan, the chan_sip translation table, and the failures that account for most of them.

VICIfastLast updated

We don't resell SIP — that's by design. You bring whichever carrier gives you the best per-minute rates, jurisdictional coverage, and CLI controls.

This page is the config-level version: the Asterisk objects a trunk is made of, and what to check when one won't pass a call. It is written for a stock VICIdial box you administer yourself.

On a VICIfast server, don't do it this way. Trunks are managed for you from the dashboard — pick a carrier template, and we render the config, reload Asterisk, and whitelist the carrier's IPs in your firewall. Those trunks live in their own managed files and deliberately do not appear under Admin → Carriers. Follow connecting your SIP carrier from the Trunks tab instead.

chan_sip is gone — this is PJSIP

Asterisk removed chan_sip in version 21. VICIfast boxes run Asterisk 22 and are PJSIP-only, and any modern VICIdial install is in the same position. If you are following a guide that hands you a [trunkname] block with type=peer, canreinvite, or nat=force_rport,comedia, it is written for a channel driver your box no longer loads — the config will be ignored and the trunk will never register.

The PJSIP equivalents, one for one:

chan_sipPJSIP
type=peerseparate endpoint + aor + auth
host=contact=sip:<host> on the AOR
username= / secret=an auth object, auth_type=userpass
canreinvite=nodirect_media=no
nat=force_rport,comediaforce_rport=yes + rtp_symmetric=yes
register => linea registration object
insecure=invitean identify object (IP auth)

Where it goes

VICIdial admin → AdminCarriersAdd Carrier. Carrier definitions are stored in vicidial_server_trunks and rendered onto disk when you save.

Each carrier needs at minimum:

  • Carrier ID — short name like twilio_us (alphanumeric + underscores)
  • Carrier Name — anything human-readable
  • Server IP — the host this carrier is reachable on. On a single-box install, 127.0.0.1.
  • ActiveY once you're ready to use it
  • Globals String — variables the rest of the config can reference
  • Registration String — for registration-based carriers only; IP-auth carriers leave it empty
  • Account Entry — the PJSIP objects
  • Dial Plan Entry — the extensions that dial out through it

Worked example: Twilio Elastic SIP Trunking

Credentials-based auth. An IP-auth carrier drops the auth and registration objects and adds an identify instead.

; Globals String
TWUSER=ACxxxxxxxxxxxxxxxxxxxxxxxxxx
TWPASS=your_termination_secret
TWHOST=your-trunk.pstn.twilio.com
; Account Entry — endpoint, auth, AOR, registration
[twilio_us]
type=endpoint
context=trunkinbound
disallow=all
allow=ulaw
allow=alaw
allow=g729
outbound_auth=twilio_us-auth
aors=twilio_us-aor
from_domain=${TWHOST}
direct_media=no
rtp_symmetric=yes
force_rport=yes
trust_id_inbound=yes
; reap carrier legs whose audio dies without a BYE
rtp_timeout=120

[twilio_us-auth]
type=auth
auth_type=userpass
username=${TWUSER}
password=${TWPASS}

[twilio_us-aor]
type=aor
contact=sip:${TWHOST}
qualify_frequency=60

[twilio_us-registration]
type=registration
outbound_auth=twilio_us-auth
server_uri=sip:${TWHOST}
client_uri=sip:${TWUSER}@${TWHOST}
retry_interval=60
forbidden_retry_interval=600
; Dial Plan Entry — 9 is the dial prefix, stripped before the number goes out
exten => _9X.,1,AGI(agi://127.0.0.1:4577/call_log)
exten => _9X.,n,Dial(PJSIP/${EXTEN:1}@twilio_us,,tor)
exten => _9X.,n,Hangup

Carriers that publish several origination IPs — Twilio does — take one contact= line per IP on the same AOR. Asterisk rotates across the contacts and skips ones that fail to qualify.

Common gotchas

Registration never succeeds

pjsip show registrations tells you where it stopped. Rejected with a 401 or 403 is credentials or a missing IP allowlist entry on the carrier's side; Unregistered with nothing in the log usually means the packet never left, which is a firewall problem.

"All circuits busy" on every test

Your carrier's signalling IPs aren't allowed inbound on 5060/UDP. Check the firewall (firewall-cmd --list-all or iptables -L -n, depending on your distro). Every carrier publishes its IP list; some rotate them, so this breaks again months later with no config change on your side.

Calls connect, but no audio

Almost always RTP. Confirm:

  • rtp_symmetric=yes and force_rport=yes on the endpoint
  • direct_media=no, so audio doesn't get re-invited straight to the carrier
  • The RTP range in rtp.conf is open in the firewall — 10000–20000 on a stock install, 10000–30000 on a VICIfast box
  • Your box has a public IP, not a NAT'd load balancer in front

403 Forbidden

Authentication. On registration carriers it's usually a typo in client_uri or the auth password. On IP-auth carriers it means the carrier doesn't recognise your source IP — add it in their portal, not yours. pjsip set logger on shows the exact response.

488 Not Acceptable Here

The carrier rejected your codec offer. ulaw, alaw, and g729 cover essentially every US and EU carrier; a hand-edited allow= line offering something exotic is the usual cause.

Multiple carriers and least-cost routing

VICIdial routes outbound on the campaign's Dial Method plus Carrier ID Override. Define each carrier separately, then pick per-campaign which one a list uses. Note that a campaign has three separate prefixes — auto dial, manual dial, and three-way — and each can point at a different carrier.

For real least-cost routing across many carriers, look at the dial_handler custom AGI. Out of scope here, but it's the supported hook.

Test before you scale

One test campaign, one phone number (yours), one agent, Auto Dial Level = 1. Confirm the call lands and you have audio both ways. Then ramp dial level and concurrent agents — a trunk that works at 1 concurrent call can still hit a channel limit at 30.