How to connect a softphone to FreePBX or Asterisk (PJSIP), step by step
FreePBX softphone setup over PJSIP: create the extension, match the Asterisk endpoint, auth and aor config, set NAT options, and fix 401, 403 and timeouts.
Published 11 min read
Connecting a softphone to FreePBX or Asterisk takes four things: a PJSIP extension with a secret, a server address and port the phone can reach, a transport (UDP, TCP or TLS) that matches that port, and NAT settings when the phone and the PBX sit on different networks. Registration shows whether the first three are right, and two CLI commands on the PBX show what the server sees. The steps below cover FreePBX's extension form, the equivalent pjsip.conf sections for plain Asterisk, and the checks to run after each step.
What you need
- Admin access to the PBX: the FreePBX web interface, or shell and Asterisk CLI access on a plain Asterisk server.
- Network reachability from the phone to the PBX on its SIP port. RFC 3261 sets the defaults at 5060 for SIP over UDP or TCP, and 5061 for SIP over TLS. The RTP media port range has to be open as well, or calls connect and then stay silent.
- A softphone. Darwvin Softphone is the example client in this guide, but any SIP softphone takes the same fields.
Step 1: Create the extension in FreePBX
FreePBX 17 uses PJSIP as its default SIP driver, and the default Asterisk 21 build does not include chan_sip. New extensions are therefore PJSIP extensions.
- Go to Applications, then Extensions.
- Open the Add Extension dropdown and choose Add New PJSIP Extension. Labels change between releases, so match them to your screen.
- Enter the extension number, a display name and a secret. The extension number is also the SIP user name, so the softphone signs in as
1001, not as the display name. - Save, then apply the configuration changes.
Make the secret long and random, and use letters and digits. Symbols are easy to mistype on a phone keyboard. Keep the secret in your password manager, because you will type it into the softphone in Step 4.
Step 2: PJSIP endpoint configuration for plain Asterisk
On a plain Asterisk server, the same extension is four sections in pjsip.conf: a transport, an endpoint, an auth section and an AOR (address of record). The shape follows the Asterisk res_pjsip examples. Change the names, codecs, context and secret to fit your system.
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
[1001]
type=endpoint
transport=transport-udp
context=internal
disallow=all
allow=opus,g722,ulaw,alaw
auth=auth1001
aors=1001
direct_media=no
rtp_symmetric=yes
rewrite_contact=yes
[auth1001]
type=auth
auth_type=userpass
username=1001
password=replace-with-a-long-random-secret
[1001]
type=aor
max_contacts=1
remove_existing=yes
Each section does one job:
- The endpoint is the identity. By default Asterisk matches an incoming request to an endpoint by the user part of the From header. The softphone's user name has to equal the endpoint name, here
1001. If a phone sends a different From user, addauth_usernametoidentify_by. The res_pjsip reference lists it among the identification methods. - The auth section is separate. The endpoint points to it with
auth=. Asterisk checks the digest againstusernameandpasswordin that section, so both must match what the softphone sends. - The AOR stores registrations.
max_contacts=1allows one registered device.remove_existing=yesreplaces the old contact when the phone registers again, which is what you want after a reboot. - The context has to exist. Calls from the endpoint go to the dialplan context named in
context=. Rundialplan show internalto confirm the context holds the extensions you expect. - Opus needs a module. Opus needs the codec_opus module, so check it with
module show like codec_opus.so. In our lab, Asterisk without codec_opus could not terminate or bridge Opus calls.
For TLS, add a transport with a certificate and a key:
[transport-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
cert_file=/etc/asterisk/keys/pbx.pem
priv_key_file=/etc/asterisk/keys/pbx.key
cert_file holds the certificate and priv_key_file holds the private key, and the two must match. Point the endpoint at the transport with transport=transport-tls. If you leave the line out, Asterisk picks the first suitable transport for the destination, which works but is less predictable. For TCP, use protocol=tcp with the same shape.
Reload rules matter here. Transport changes take effect only after a full Asterisk restart, so run core restart now or restart the service. A module reload does not apply them. For endpoint, auth and AOR changes, reload the PJSIP module with module reload res_pjsip.so.
Step 3: Check the endpoint on the Asterisk CLI
Connect to the console with verbose output:
asterisk -rvvv
Then run:
pjsip show endpoints
pjsip show endpoint 1001
pjsip show aor 1001
Read the output this way:
- The endpoint appears in
pjsip show endpoints. If it does not, look for ERROR lines in the startup log, and check that the section containstype = endpoint. Asterisk ignores a section without it. pjsip show endpoint 1001lists anInAuth:line, which shows the auth section is attached, and anAor:line that points at the AOR.- Before the phone registers, the AOR has no contact. After a successful REGISTER,
pjsip show aor 1001shows aContact:line with the phone's address. Contact lines also appear in the endpoint output.
When something fails, turn on logging, reproduce the attempt and read the messages:
core set verbose 4
core set debug 4
pjsip set logger on
pjsip set logger on prints every SIP message to the console. Turn it off with pjsip set logger off once you have what you need.
Step 4: Point the softphone at the PBX
The fields are the same in every softphone, even when the labels differ:
| Softphone field | Value | Notes |
|---|---|---|
| User name | 1001 | The extension number. It must match the endpoint name, or identify_by has to cover it. |
| Password | The secret | Must match the auth section exactly, including case. |
| Server | pbx.example.com or an IP address | The name must resolve from the phone's network. |
| Port | 5060 for UDP or TCP, 5061 for TLS | Set it only if you changed the PBX port. |
| Transport | UDP, TCP or TLS | Must match the transport the PBX listens on. |
| Auth user | 1001 | Only if it differs from the user name. |
The setup guide for the example client uses the same inputs: user name, password and server, with Transport on Auto unless the PBX needs a specific one. On Linux, open Settings, then Accounts. On Android, tap Add Account, then Manual SIP Setup.
Step 5: Set NAT options when the phone or the PBX is behind NAT
NAT breaks SIP in two places. The reply to a REGISTER has to go back to the port the request came from, not to the port the phone claims to use. And the media addresses in the SDP can be private addresses the other side cannot reach. RFC 3581 defines the rport mechanism, and force_rport is Asterisk's way of applying it.
Asterisk's defaults are not NAT-friendly. rtp_symmetric and rewrite_contact default to no, and direct_media defaults to yes. These are the options that matter:
| Option | Where | What it does |
|---|---|---|
local_net | transport | Lists your internal networks. Addresses inside them are not rewritten. |
external_signaling_address | transport | The public address Asterisk uses in SIP messages sent outside local_net. |
external_media_address | transport | The public address Asterisk uses for RTP in SDP sent outside local_net. |
rtp_symmetric | endpoint | Sends RTP back to the address and port it arrived from. |
rewrite_contact | endpoint | Uses the address and port the request arrived from in the Contact header. |
force_rport | endpoint | Sends SIP responses back to the port the request came from. The default is yes. |
direct_media | endpoint | Set it to no to keep Asterisk in the media path, as the NAT examples do. |
A NAT-ready UDP transport looks like this. It replaces the plain transport from Step 2, and the endpoint keeps transport=transport-udp:
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
local_net=192.168.0.0/16
external_signaling_address=203.0.113.10
external_media_address=203.0.113.10
Replace 203.0.113.10, which is a documentation address, with your public address. Then restart Asterisk, because transport changes do not take effect on a module reload.
On FreePBX, do not hand-edit the generated pjsip.conf, since FreePBX rewrites it. Forum answers name FreePBX's custom PJSIP files for this job: pjsip.endpoint_custom.conf for hand-written endpoints, and pjsip.endpoint_custom_post.conf for endpoints made in the GUI. Check which one your release reads, then confirm the result with pjsip show endpoint.
Step 6: Test registration and audio
- The softphone shows the account as registered.
- On the PBX,
pjsip show aor 1001lists a Contact line. - Call an echo test or another extension, and check that audio runs both ways.
If the call connects but one side hears nothing, the cause is usually NAT or the RTP ports rather than SIP. Work through one-way audio on VoIP calls before you change the PBX again.
Failures you will see, and what they mean
| What you see | What it means | Fix |
|---|---|---|
| 401 Unauthorized, again after the phone answers the challenge | RFC 3261 uses 401 to ask for credentials. Asterisk also sends a 401 challenge to a request it cannot match to any endpoint. A challenge, a retry and another 401 point to an unmatched request, not necessarily a wrong password. | Check that the endpoint shows InAuth: and Aor:. Search the log for "Could not identify endpoint by username" or "does not match identify". Fix the From user or identify_by first, then check the password, its spelling and its case. |
| 403 Forbidden on REGISTER | RFC 3261 says the server understood the request and refuses it, and adding credentials will not change the outcome. In Asterisk, the usual causes are an AOR with no max_contacts, or a registration that would exceed it. | Set max_contacts on the AOR, typically 1 for one phone. The log says "has no configured max_contacts" or "will exceed max contacts". |
| No response, then a timeout | No reply reached the phone. RFC 3261 says a non-INVITE client waits 64*T1 seconds before it gives up. With T1 at its default of 500 ms, that is 32 seconds. Some stacks report the timeout as a locally generated 408 Request Timeout. | Check the server name, port and transport. Make sure UDP 5060 or TLS 5061 reaches the PBX through every firewall. On the PBX, run tcpdump -ni any port 5060 while you retry, and see whether the REGISTER arrives. |
| TLS fails straight away | Transport and port disagree. TLS normally uses 5061, while UDP and TCP use 5060. | Match the transport and the port on both ends. |
| 488 Not Acceptable Here on the call | There is no codec in common between the phone and the PBX. | Add codecs both sides support to allow=. Check codec_opus if you want Opus. |
| Registered, but one side is silent | NAT or the RTP ports. | Review Step 5, then the one-way audio guide. |
To see what a 403 looks like on purpose, set max_contacts=0 on a test AOR. With the right password, the phone then gets a 403. We reproduced this in our lab. For the full list of response codes, see SIP response codes explained.
FAQ
Should I use chan_sip or PJSIP on FreePBX 17?
Use PJSIP. FreePBX 17 uses it by default, and the default Asterisk 21 build does not include chan_sip. If you still have chan_sip devices, check how your release migrates them before you change anything.
Which transport should I choose?
UDP is the simplest on a private network. TCP keeps a connection open, and TLS also encrypts the SIP signalling, on port 5061 by default. Choose a transport that both the PBX offers and the phone supports, and match it on both sides. Use TLS when SIP crosses the internet.
Why does registration work but calls have no audio?
Signalling and media take different paths. A successful registration shows that the SIP path works. One-way or no audio usually means RTP cannot reach the other side, because of NAT or closed RTP ports. Check the NAT options in Step 5, confirm that direct_media=no is set, and open the RTP range. The one-way audio guide has the full checklist.
Does the app work with FreePBX?
FreePBX is built on Asterisk, and we test the app against Asterisk. Other PBXs are not qualified yet, FreePBX included. The app uses standard SIP, so a PJSIP extension configured as in this guide is the right starting point. On Linux, Darwvin Softphone's diagnostics show a SIP message ladder, so you can follow the SIP messages of a failed call in order.
References
- RFC 3261: Session Initiation Protocol. Default ports (section 19.1.1), the 401, 403 and 408 responses (sections 21.4.2, 21.4.4 and 21.4.9), and Timer F (section 17.1.2.2).
- RFC 3581: An Extension to SIP for Symmetric Response Routing. The rport mechanism behind
force_rport. - Asterisk: PJSIP configuration sections and relationships
- Asterisk: res_pjsip configuration examples
- Asterisk: PJSIP troubleshooting guide
- Asterisk: configuring res_pjsip to work through NAT
- Asterisk: res_pjsip module reference. Transport options and reload behaviour.
- Sangoma knowledge base: Extensions module, PJSIP extension
- Sangoma knowledge base: using FreePBX 17 with chan_sip