SIP trunk registration and variables
This chapter connects a PBX or a carrier trunk to the gateway over SIP. There are two ways to do it. Pick one, set the gateway variables, configure the PBX, then check the log lines at the end.
Two ways to connect
| Direct to the gateway bridge | Asterisk in front of the gateway | |
|---|---|---|
| Software on the gateway host | Gateway only | Gateway plus Asterisk |
| Codecs | G.711 only (mu-law, A-law) | Opus with FEC, AMR-WB, mu-law, A-law |
| Lossy or long-distance links | Plain G.711 | Opus with FEC or AMR-WB tolerates loss better |
| IVR, queues, recording | Not available | In the PBX |
| Transfer to a human | SIP REFER only | Inside the PBX, or REFER |
| Co-hosting with a PBX | Possible; RTP ranges must not overlap | Possible; RTP ranges must not overlap |
PBX trunk qualify_frequency |
Leave off | 15 |
Choose the direct bridge when a call should go from the phone line to the bot and back, and nothing else. Choose an Asterisk in front when you need Opus, IVR menus, queues or call recording. The Asterisk route is covered in Asterisk and AudioSocket; this chapter only covers the trunk between a customer PBX and that Asterisk.
Gateway side
Set these in .env for the direct bridge:
TELEPHONY_MODE=sip_bridge
SIP_PORT=5080
RTP_PORT_MIN=20002
RTP_PORT_MAX=30000
SIP_PUBLIC_ADDRESS=
SIP_ALLOWED_IPS=
SIP_CODEC=0
SIP_MAX_CALLS=0
SIP_RING_DURATION=0
TRANSFER_SIP_URI=
| Variable | What it does |
|---|---|
TELEPHONY_MODE |
audiosocket (built-in default), sip_bridge, or both. The bridge only starts when this is sip_bridge or both. |
SIP_PORT |
UDP port the bridge listens on. This manual sets 5080; if the variable is unset the bridge falls back to 5060. |
RTP_PORT_MIN, RTP_PORT_MAX |
Range of even UDP ports used for call audio. The fallback is 10000 to 20000. |
SIP_PUBLIC_ADDRESS |
The address written into SDP. Behind NAT it must be the public IP, otherwise audio is one-way. Falls back to PUBLIC_IP when empty. |
SIP_ALLOWED_IPS |
Comma-separated exact IP addresses (no CIDR ranges) allowed to send INVITEs; a CIDR entry matches nothing and blocks every call. Empty means any source is accepted. |
SIP_CODEC |
0 for PCMU (mu-law, default) or 8 for PCMA (A-law). |
SIP_MAX_CALLS |
Concurrent SIP calls. 0 means unlimited. Beyond the limit the gateway answers 503 and logs Rate limit reached: N/M. The licence cap applies separately. |
SIP_RING_DURATION |
Milliseconds of ringing before the gateway answers. 0 answers immediately. |
TRANSFER_SIP_URI |
REFER target for transfers, for example sip:7000@<pbx-ip>. The HTTP TRANSFER_ENDPOINT serves AudioSocket calls only and is never used as a REFER target. |
Set SIP_ALLOWED_IPS to your PBX or carrier address on any gateway reachable from the internet. Leaving it empty lets anyone place calls that spend your STT, LLM and TTS budget.
Restart the gateway after editing .env.
Do not let the RTP ranges overlap
The gateway's fallback RTP range (10000 to 20000) is the same range Asterisk uses by default. On a host that runs both, a port already taken by Asterisk makes the gateway retry on another port. Separate the ranges: move the gateway to 20002-30000 as shown above, and check the PBX's rtpstart and rtpend in rtp.conf so the two ranges stay apart. Open the gateway range in the firewall, not Asterisk's.
Registering to a carrier
Some carriers and PBXs make the gateway register instead of sending INVITEs to a fixed address. Set the registration variables and the gateway sends REGISTER on boot and repeats it:
TELEPHONY_MODE=sip_bridge
SIP_REGISTER_URI=sip:trunk.example.com
SIP_AUTH_USER=your_username
SIP_AUTH_PASSWORD=your_password
SIP_REGISTER_INTERVAL=3600
SIP_KEEPALIVE_URI=
SIP_KEEPALIVE_INTERVAL=30000
| Variable | What it does |
|---|---|
SIP_REGISTER_URI |
The registrar, with the sip: prefix (sip:domain). Leave empty if the carrier sends calls to you without registration. |
SIP_AUTH_USER, SIP_AUTH_PASSWORD |
Credentials for the registrar and for any outbound auth challenge. |
SIP_REGISTER_INTERVAL |
Seconds between re-registrations. Default 3600. |
SIP_KEEPALIVE_URI |
Where to send SIP OPTIONS keep-alives. Empty disables them. |
SIP_KEEPALIVE_INTERVAL |
Milliseconds between keep-alives. Default 30000. |
On success the log shows:
INFO: SIP Registration successful with sip:trunk.example.com
If you see SIP Registration failed instead, check the user and password with the carrier, check that SIP_REGISTER_URI includes sip:, and check that the carrier can reach SIP_PORT.
Outbound calls over SIP need a route as well: SIP_REGISTER_URI or SIP_OUTBOUND_PROXY, and optionally SIP_FROM_USER for the caller ID. See Outbound SIP calls for how the number is routed, and HTTP API for POST /outbound/initiate.
Customer PBX side
The PBX needs a trunk that sends INVITEs to the gateway. The two blocks below are for FreePBX or plain Asterisk with chan_pjsip; for Panasonic, Avaya, Grandstream or Bitvoice see their chapter. In FreePBX, put them in pjsip_custom.conf, or build the same settings in the GUI (see the end of this section). Use one block or the other: both are named [vexyl-gw]. The context in each only receives calls the gateway itself sends (transfers, outbound) and must exist in your dialplan. Replace <gateway-ip> with the gateway host's address and <your-udp-transport> with the transport name your PBX already uses.
Trunk to the gateway bridge (direct, G.711)
; Direct to the gateway SIP bridge. G.711 only.
; Leave qualify_frequency at 0: with qualify on, this trunk can show
; Unavailable and Dial() refuses it.
[vexyl-gw]
type=endpoint
context=from-vexyl-gw
transport=<your-udp-transport>
aors=vexyl-gw
disallow=all
allow=ulaw,alaw
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
[vexyl-gw]
type=aor
contact=sip:<gateway-ip>:5080
qualify_frequency=0
[vexyl-gw]
type=identify
endpoint=vexyl-gw
match=<gateway-ip>
Trunk to an Asterisk in front of the gateway
; Via an Asterisk in front of the gateway. Opus with FEC, G.711 fallback.
; qualify_frequency=15 sends an OPTIONS every 15 seconds. This keeps the
; NAT pinhole open so the bot's hang-up BYE reaches the PBX.
[vexyl-gw]
type=endpoint
context=from-vexyl-gw
transport=<your-udp-transport>
aors=vexyl-gw
disallow=all
allow=opus,ulaw,alaw
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
[vexyl-gw]
type=aor
contact=sip:<gateway-ip>:5060
qualify_frequency=15
[vexyl-gw]
type=identify
endpoint=vexyl-gw
match=<gateway-ip>
Use the port your front Asterisk listens on in place of 5060 if it differs. Without qualify_frequency=15, calls longer than about a minute never hang up on the PBX side when the bot ends them: the BYE is dropped at the NAT and the caller waits in silence.
Reload after editing. Asterisk 16 has no pjsip reload:
asterisk -rx "module reload res_pjsip.so"
asterisk -rx "pjsip show endpoint vexyl-gw"
On the Asterisk-front trunk, pjsip show contacts should show the contact as Avail. On the direct trunk with qualify_frequency=0 it shows no round-trip time, which is correct.
Inbound route to the bot
Send only the bot numbers to the trunk. Use a dedicated context and match only those numbers, not every number on the PBX:
[to-vexyl-bot]
exten => _X.,1,NoOp(To the bot)
same => n,Set(SESSION_UUID=${TRIM(${SHELL(uuidgen)})})
same => n,Dial(PJSIP/${EXTEN}@vexyl-gw,60,b(vexyl-hdr^s^1(${SESSION_UUID})))
same => n,Hangup()
[vexyl-hdr]
exten => s,1,Set(PJSIP_HEADER(add,X-Session-UUID)=${ARG1})
same => n,Return()
The header is added in a pre-dial handler (b(...)) because a Set before Dial() runs on the inbound channel and never reaches the outgoing INVITE. In FreePBX, point the inbound route's Custom Destination at to-vexyl-bot,${EXTEN},1. Create the trunk in pjsip Settings:
- General: SIP Server =
<gateway-ip>, SIP Server Port =5080(or the front Asterisk's port), Authentication = None, Registration = None. - Advanced: Qualify Frequency =
0(direct) or15(front), Rewrite Contact = Yes, RTP Symmetric = Yes, Force rport = Yes, Direct Media = No, Match (Permit) =<gateway-ip>. - Codecs: ulaw, alaw (add opus for the front).
A generic carrier
A carrier that only needs an address: ask it to send INVITEs to <gateway-ip>:5080 (UDP), offer PCMU or PCMA, and give it the number or numbers. Add the carrier's signalling address to SIP_ALLOWED_IPS. If the carrier requires you to register, use the previous section instead.
Passing the session id and bot
The X-Session-UUID header on the INVITE ties the call to metadata you posted earlier with POST /session/<uuid>/metadata on the HTTP port (HTTP_PORT; this manual uses 8081). Post the metadata first, then dial. HTTP_HOST defaults to 127.0.0.1; set it to 0.0.0.0 if the PBX is on another host. The POST example is in Send caller details first.
The bot is chosen with "botId" in that metadata POST, or with an X-Bot-Id header on the INVITE. Headers can also set the language, voice, model and recogniser for the call without any POST; see Per-call settings reference. When both are present, the posted metadata wins.
With no X-Session-UUID, the gateway makes its own session id and the call has no pre-posted metadata. With the header, the log shows Using X-Session-UUID from SIP header. The metadata endpoint is described in HTTP API; what a bot does with it is in Conversation behaviour.
Add the header with the pre-dial handler shown in the inbound route above (b(vexyl-hdr^s^1(${SESSION_UUID}))), using the same value you posted the metadata under. Generate the id with uuidgen as shown (package uuid-runtime, Asterisk module func_shell): the gateway accepts any string here, but an Asterisk placed in front of the gateway forwards this value into AudioSocket(), which requires a real UUID.
Firewall
Allow only the PBX or carrier address. Never expose 8081 to the internet.
ufw allow from <pbx-ip> to any port 5080 proto udp
ufw allow from <pbx-ip> to any port 20002:30000 proto udp
ufw allow from <pbx-ip> to any port 8081 proto tcp
The last line is only needed if the PBX posts session metadata. If the gateway is behind a NAT router, forward 5080/udp and the RTP range to it as well, and set SIP_PUBLIC_ADDRESS to the router's public IP. Calls cannot come from a carrier whose address is not in the rules above.
What a working call looks like
A healthy inbound call writes these lines, in this order:
INFO: SIP Bridge started on port 5080
INFO: SIP Registration successful with sip:trunk.example.com
INFO: Using X-Session-UUID from SIP header: 3f2a9c1e-0000-0000-0000-000000000000
INFO: SIP call connected - ID: 1b7e..., Caller: 919800000000
The first line appears at boot. The registration line only appears when you set SIP_REGISTER_URI. The X-Session-UUID line only appears when the PBX sent the header. The last line appears for every answered call.
When the call limit is reached the gateway rejects the INVITE and logs:
SIP call rejected due to concurrent limit: 30/30
When it does not work
| What you see | Likely cause | Fix |
|---|---|---|
| No INVITE in the gateway log | Firewall closed, wrong port, or the PBX is sending to 5060 |
Open 5080/udp from the PBX. Check the trunk contact port. Watch with tcpdump -n udp port 5080 or sngrep. |
Blocked request from unauthorized IP: <addr> (the PBX gets 403 Forbidden) |
The source is not in SIP_ALLOWED_IPS |
Add exactly that address to SIP_ALLOWED_IPS. |
SIP call rejected due to concurrent limit: N/M |
Licence cap reached | Wait for calls to end, or add a gateway instance. |
Rate limit reached: N/M (the PBX gets 503) |
SIP_MAX_CALLS reached |
Raise SIP_MAX_CALLS. |
| Call answers but one-way audio or silence | RTP range closed, or SIP_PUBLIC_ADDRESS unset behind NAT |
Open the RTP range from the PBX. Set SIP_PUBLIC_ADDRESS to the public IP. Check with tcpdump -n udp portrange 20002-30000. |
| Port-bind retries in the gateway log on a host shared with Asterisk | PBX RTP range overlaps the gateway's | Set gateway RTP_PORT_MIN and RTP_PORT_MAX to 20002 and 30000, and check rtp.conf. |
| Trunk shows Unavailable on a direct bridge, calls fail at once | qualify_frequency is set on a direct-to-bridge trunk |
Set qualify_frequency=0 on the aor and reload. Check with pjsip show contacts. |
| Bot hangs up but the caller stays on the line (long calls) | NAT drops the BYE on an Asterisk-front trunk | Set qualify_frequency=15 on that aor and reload. |
SIP Registration failed |
Wrong credentials or registrar URI | Re-check SIP_AUTH_USER, SIP_AUTH_PASSWORD, SIP_REGISTER_URI (needs sip:). |
| Transfer does nothing | TRANSFER_SIP_URI empty |
Set it to the PBX extension, and make the PBX accept REFER from the gateway. |
Next: Panasonic, Avaya, Grandstream and Bitvoice for other IP PBXs, Outbound SIP and per-call settings, Asterisk and AudioSocket for the Asterisk-front route, and HTTP API for metadata and outbound calls.