# Connect your PBX

Once you have created your trunk, you configure your PBX to use it. The PBX needs to know four things:
where to send calls, how to prove who it is, which addresses to accept calls and audio from, and
which codecs to use. This page gives those settings for Asterisk with PJSIP, FreePBX and 3CX, the
firewall rules to open, and a troubleshooting guide.

The examples use the trunk from the [quickstart](/sip-trunks/quickstart): `office-pbx`, a PBX at
`203.0.113.10` that accepts SIP over TLS on port 5061.

## Collect your values

Read the platform values from `GET /v1/platform/endpoints` and the trunk values from
`GET /v1/trunks/{trunk_id}`. Do not copy the values below into production without checking them
against the API: PressOne can change platform addresses, and the response's `version` and `as_of`
tell you when.

| Setting | Example value | Where it comes from |
| --- | --- | --- |
| SIP server | `sip.trunk.pressone.co` | `signaling_fqdn` in platform endpoints |
| Trunk SIP domain | `office-pbx.sip.trunk.pressone.co` | `signaling_fqdn` on the trunk |
| Signalling addresses | The addresses in `signaling_nodes` | `signaling_nodes` |
| SIP ports | 5061 for TLS, 5060 for UDP or TCP | `signaling_ports` |
| TLS name | `sip.trunk.pressone.co` | `tls.realm` |
| Media addresses | The ranges in `media_cidrs` | `media_cidrs` |
| Signals RTP ports | UDP 10000 to 20000 | `rtp_ports` |
| Codecs | G.711 A-law (PCMA) and mu-law (PCMU), 20 ms, plus any of G729, G722 and OPUS you added | `codecs` on the trunk; G.711 is always offered |
| DTMF | RFC 4733 telephone events | |
| Number format | E.164 with a leading `+`, for example `+2348034128890` | |
| Caller ID | A number routed to the trunk, for example `+2342013300100` | [Numbers and routing](/sip-trunks/numbers-and-routing) |
| Authentication | Your PBX's public address, or a digest username, password and realm | [Authentication and security](/sip-trunks/authentication-and-security) |

## Open your firewall

Your firewall must let SIP and audio pass between your PBX and Signals. TLS runs over TCP, so the
TLS port is a TCP port.

| Direction | Protocol and ports | From | To | Why |
| --- | --- | --- | --- | --- |
| Out | TCP 5061 (TLS), or UDP and TCP 5060 | Your PBX | `signaling_nodes` | Your PBX sends calls to Signals. |
| In | The port in your termination target, such as TCP 5061 | `signaling_nodes` | Your PBX | Signals delivers inbound calls. |
| Out | UDP, your PBX's RTP ports | Your PBX | `media_cidrs`, UDP 10000 to 20000 | Audio to Signals. |
| In | UDP 10000 to 20000 from Signals, to your PBX's RTP ports | `media_cidrs` | Your PBX | Audio from Signals. |

:::note
UDP 10000 to 20000 is the range of ports Signals sends audio from and receives audio on, so it is
the range to allow from the Signals media address. It does not limit how many calls you can run.
Concurrency is set by your trunk and outbound policy limits.
:::

If your PBX sits behind NAT, it must advertise its public address in SIP and SDP, and keep audio
flowing through itself rather than directly between phones and Signals. The settings below cover
both.

## Asterisk with PJSIP

These settings target Asterisk 18 or later with `chan_pjsip`. They define a TLS transport, the
Signals endpoint, its address of record (AOR) and an identify section that matches calls coming
from Signals.

### IP allow list trunk

Add this to `pjsip.conf`. Replace the certificate paths and `203.0.113.10` with your own:

```ini
; Signals trunk office-pbx over TLS, IP allow list authentication.
[signals-tls]
type = transport
protocol = tls
bind = 0.0.0.0:5061
cert_file = /etc/asterisk/keys/pbx.example.com.ng.crt
priv_key_file = /etc/asterisk/keys/pbx.example.com.ng.key
method = tlsv1_2
external_signaling_address = 203.0.113.10
external_media_address = 203.0.113.10

[signals]
type = endpoint
transport = signals-tls
context = from-signals
aors = signals
disallow = all
allow = alaw,ulaw
dtmf_mode = rfc4733
direct_media = no
rtp_symmetric = yes
force_rport = yes
rewrite_contact = yes
from_domain = office-pbx.sip.trunk.pressone.co

[signals]
type = aor
contact = sip:sip.trunk.pressone.co:5061

[signals]
type = identify
endpoint = signals
match = 198.51.100.10/32   ; each range in media_cidrs and signaling_nodes
```

- `bind = 0.0.0.0:5061` with your certificate lets Signals deliver inbound calls over TLS, matching
  the `tls` termination target.
- `direct_media = no` keeps audio on the PBX, which NAT and the Signals media address require.
- `identify` with `match` tells Asterisk that SIP from the Signals signalling address belongs to this
  endpoint. Add one `match` line per address in `signaling_nodes`.
- There is no registration. Signals reaches your PBX through the termination targets on the trunk.

If your trunk does not require TLS, use a UDP transport on port 5060 instead and set
`contact = sip:sip.trunk.pressone.co:5060`.

### Digest trunk

For a digest trunk, add an auth section with the credential Signals generated, and point the
endpoint at it with `outbound_auth`. Keep the transport, AOR and identify sections from above:

```ini
[signals-auth]
type = auth
auth_type = userpass
username = sig_01992f3a91b07c558e214a6d0f3b9c18
password = PASTE_THE_PASSWORD_SHOWN_ONCE
realm = sip.trunk.pressone.co

[signals]
type = endpoint
transport = signals-tls
context = from-signals
aors = signals
outbound_auth = signals-auth
disallow = all
allow = alaw,ulaw
dtmf_mode = rfc4733
direct_media = no
rtp_symmetric = yes
force_rport = yes
rewrite_contact = yes
from_domain = office-pbx.sip.trunk.pressone.co
```

When you rotate the credential, replace `username` and `password` and run
`pjsip reload` before the old credential's `previous_retires_at`.

### Encrypted audio

If the trunk uses `"media_security": "sdes_srtp_required"`, add this line to the endpoint:

```ini
media_encryption = sdes
```

Use it only with the TLS transport. SDES sends the audio keys inside SIP messages.

### Dialplan

Send outbound calls in E.164 format with a caller ID routed to the trunk. This example also turns a
Nigerian national number such as `08034128890` into `+2348034128890`:

```ini
[to-signals]
exten => _+X.,1,Set(CALLERID(num)=+2342013300100)
 same => n,Dial(PJSIP/${EXTEN}@signals,60)
 same => n,Hangup()

exten => _0XXXXXXXXXX,1,Set(CALLERID(num)=+2342013300100)
 same => n,Dial(PJSIP/+234${EXTEN:1}@signals,60)
 same => n,Hangup()
```

Inbound calls arrive in the `from-signals` context with the called number in the request URI. This
example rings extension 1001 for any number. Place a test call and check the Asterisk console for
the exact format of the called number, then match it per number:

```ini
[from-signals]
exten => _X.,1,Dial(PJSIP/1001,30)
 same => n,Hangup()
exten => _+X.,1,Dial(PJSIP/1001,30)
 same => n,Hangup()
```

## FreePBX

FreePBX builds the same PJSIP configuration from its web interface. Menu and field names below
follow recent FreePBX releases and may differ in yours.

1. In **Settings**, open the SIP settings for `chan_pjsip`. Set your external (public) address and
   your local networks so FreePBX rewrites addresses behind NAT. If your trunk requires TLS, enable
   the TLS transport on port 5061 with a certificate from the certificate manager.
2. In **Connectivity**, add a PJSIP trunk named `signals`.
3. On the trunk's general settings, set the outbound caller ID to a routed number, such as
   `+2342013300100`.
4. On the PJSIP settings:
   - Set the SIP server to `sip.trunk.pressone.co` and the port to `5061` for TLS, or `5060` for UDP.
   - Choose the transport that matches the port.
   - Turn registration off.
   - For an IP allow list trunk, set authentication to none. For a digest trunk, set outbound
     authentication with the username and password Signals generated.
   - Set the inbound context to your inbound routes context (FreePBX uses `from-pstn`).
5. On the advanced PJSIP settings, set the match or permit address to the ranges in `signaling_nodes` and `media_cidrs`, turn
   direct media off, turn RTP symmetric and rewrite contact on, and set the from domain to the
   trunk's `signaling_fqdn`. If the trunk requires SRTP, set media encryption to SDES (SRTP via
   in-SDP).
6. On the codecs tab, allow only `alaw` and `ulaw`.
7. Create an outbound route that sends numbers in E.164 format through the `signals` trunk. Add a
   dial pattern that prefixes `+234` and strips the leading `0` from national numbers.
8. Create inbound routes for the numbers you routed to the trunk.
9. Apply the configuration.

## 3CX

3CX menu names change between versions, so this section lists the settings rather than the exact
screens. Add a SIP trunk using a generic SIP trunk template, then set:

| 3CX setting | Value |
| --- | --- |
| Registrar, server or proxy | `sip.trunk.pressone.co` |
| Port | `5061` for TLS, `5060` for UDP or TCP |
| Transport | TLS if your 3CX version offers it for SIP trunks; otherwise UDP, with `require_tls` set to `false` on the trunk |
| Authentication | IP-based (no registration) for an IP allow list trunk; username and password for a digest trunk |
| Authentication ID and password | The digest `username` and `password`, for digest trunks only |
| Main trunk number | A number routed to the trunk, such as `+2342013300100` |
| Allowed codecs | G.711 A-law and G.711 U-law only |
| Outbound caller ID | A number routed to the trunk |

Then create outbound rules that send calls through the trunk in E.164 format with a `+`, and inbound
rules (DID rules) for each routed number or range. Allow the Signals signalling and media addresses
in any firewall in front of 3CX, as listed in [Open your firewall](#open-your-firewall).

:::caution
If your 3CX cannot use TLS for SIP trunks, create the trunk with `"require_tls": false`, a `udp` or
`tcp` termination target, and `"media_security": "rtp"`. SRTP with SDES is not safe without TLS.
:::

## Verify the connection

1. Wait a minute after your last change so it has taken effect.
2. Run a configuration test: `POST /v1/trunks/{trunk_id}/tests` with
   `{ "kind": "config_validation" }`, then read the test.
3. Place an outbound call to a Nigerian mobile number from an extension.
4. Call one of your routed numbers from a mobile phone and confirm it rings the PBX.
5. Check `GET /v1/trunks/{trunk_id}/diagnostics` and the trunk's `endpoint_evidence` after a few
   calls.

## Troubleshooting

Start with the API. It tells you how the trunk is configured and what the network saw on real calls.

### Outbound calls are rejected before they ring

- **The change is very recent.** A saved change takes effect within a minute. If a change cannot be
  applied, PressOne staff are alerted and the previous configuration keeps serving, so a call can
  still follow your earlier settings for a short time.
- **Your address is not allowed.** Calls must come from an address in the trunk's IP allow list. Behind NAT, your traffic may leave from a different public address than you expect.
  Compare it with `GET /v1/trunks/{trunk_id}/ip-credentials`.
- **Digest fails.** Check the username, password and realm. A new credential works once the network
  has accepted it (`new_applied_at` is set), within a minute. A rotated credential stops at
  `previous_retires_at`, and a revoked one stops within a minute of the revoke.
- **TLS fails.** Confirm the PBX connects to port 5061 over TLS and trusts the Signals certificate.

### Outbound calls are refused after authentication

- **No policy.** The trunk's `policy` must be set. A trunk without a policy refuses every outbound
  call.
- **Destination not allowed.** The number must match an allow rule in the policy, and no more
  specific deny rule. Send numbers in E.164 format.
- **Caller ID not allowed.** The caller ID must be a number routed to the trunk, with its route
  `status` `applied`. A trunk's `numbers` count of 0 means it has none.
- **A limit was reached.** Check `concurrency`, `cps` and the daily spend cap on the trunk, the
  policy and any subtrunk. `ceilings.daily_spend.used_today_minor` on the policy shows today's spend.
- **The trunk is suspended.** `status` is `suspended`. Call `POST /v1/trunks/{trunk_id}/resume`.

### Inbound calls do not arrive

- **The route is not applied.** `GET /v1/numbers/blocks/{block_id}/routes` should show the number's
  slice with the trunk as its `effective` target and `status` `applied`.
- **The trunk does not take inbound calls.** Its `direction` must be `inbound` or `both`, with at
  least one enabled termination target.
- **Signals cannot reach your PBX.** Allow SIP from the Signals signalling address to the port in
  your termination target. For a `tls` target, your PBX needs a valid certificate on that port.
- **Target health.** The trunk's `endpoint_evidence` shows each target as `healthy`, `degraded`,
  `idle` or `unknown`, with the last failure code. Signals does not probe targets, so a target shows
  `idle` until real calls reach it.

### One-way audio or no audio

- Allow UDP in both directions between your PBX's RTP ports and the Signals media address on ports
  10000 to 20000.
- Set your public address for media (`external_media_address` in Asterisk) and keep direct media
  off.
- `GET /v1/trunks/{trunk_id}/diagnostics` counts `one_way_calls` and `no_media_calls` over the last
  24 hours, alongside MOS, packet loss and jitter.

### Calls fail when the trunk requires SRTP

With `"media_security": "sdes_srtp_required"`, your PBX must offer SRTP with SDES keys. In Asterisk,
set `media_encryption = sdes` on the endpoint and use the TLS transport.

### Calls end at the same length every time

A call ends when it reaches `max_call_duration_seconds`. The shortest value across the trunk, the
policy and any subtrunk applies.

### Contacting support

Every API error is an `application/problem+json` body with a `code` and a `request_id`, and every
response carries an `X-Request-ID` header. Include the request ID, the trunk ID and the time of the
call when you contact PressOne support.

## Related

- [Authentication and security](/sip-trunks/authentication-and-security)
- [Platform API reference](/api/platform)
- [SIP Trunking API reference](/api/sip-trunking)
