Tone docs

Asterisk and FreePBX

Add Tone as a PJSIP trunk on Asterisk or FreePBX, and route selected extensions or DIDs to an agent.

If your numbers already land on an Asterisk or FreePBX box, Tone becomes one more trunk on it. This page has the config, both hand-written PJSIP and the FreePBX fields that generate it.

Before you start

  • Asterisk 16 or later with res_pjsip (the chan_sip channel driver is deprecated and removed in Asterisk 21 — use PJSIP).
  • A static public IPv4 address on the Asterisk box, or a NAT whose public address is fixed.
  • A Tone organization with KYC verified and SIP trunking enabled by Tone support.

Create the trunk in Tone first

You need the credentials before you can write the Asterisk config. Digest is the easier identity here, because a self-hosted PBX often sits behind a dynamic or shared address:

POST /v1/sip-trunks
{
  "name": "Asterisk — office",
  "inboundAuth": "digest",
  "outboundHost": "pbx.yourcompany.example",
  "outboundPort": 5061,
  "outboundTransport": "tls",
  "outboundNumberFormat": "e164",
  "callerIdPolicy": "did",
  "codecs": ["PCMA", "PCMU"],
  "maxConcurrent": 10
}

The response carries connection.username, connection.password and connection.realm once. Copy them straight into the config below.

If your box does have a fixed address, prefer "inboundAuth": "both" with an ipAcl — it is one more thing an attacker has to have, and it costs you nothing.

PJSIP configuration

pjsip.conf — replace t_9f2c… and the password with your connection values:

Tone does not require registration, so there is no type = registration section here at all. If your NAT needs a keepalive, the AOR's qualify_frequency below provides one.

[tone-auth]
type = auth
auth_type = userpass
username = t_9f2c…
password = <from connection.password>
realm = sip.usetone.ai

[tone-aor]
type = aor
contact = sip:sip.usetone.ai:5061\;transport=tls
qualify_frequency = 60

[tone-endpoint]
type = endpoint
context = from-tone
transport = transport-tls
aors = tone-aor
outbound_auth = tone-auth
disallow = all
allow = alaw
allow = ulaw
dtmf_mode = rfc4733
direct_media = no
rtp_symmetric = yes
force_rport = yes
rewrite_contact = yes
from_domain = sip.usetone.ai

[tone-identify]
type = identify
endpoint = tone-endpoint
match = <Tone signalling IP 1>
match = <Tone signalling IP 2>

Three lines are load-bearing:

  • direct_media = no. Asterisk must stay in the media path. If it re-invites the two ends to talk directly, Tone's gateway and your carrier will try to send RTP to each other and neither will be expecting it.
  • dtmf_mode = rfc4733 — this is RFC 2833 under its current name. inband will not reach the agent as digits, so DTMF-based opt-out stops working.
  • [tone-identify] with Tone's signalling IPs, from your trunk's page in the dashboard. Without it, inbound calls from Tone match no endpoint and land in the anonymous context.

Your transport, if you do not already have a TLS one:

[transport-tls]
type = transport
protocol = tls
bind = 0.0.0.0:5061
cert_file = /etc/asterisk/keys/pbx.crt
priv_key_file = /etc/asterisk/keys/pbx.key
method = tlsv1_2

Dialplan

Inbound from Tone — calls the agent placed, arriving at your PBX:

[from-tone]
exten => _X.,1,NoOp(Call from Tone for ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN}@your-carrier-trunk,60)
 same => n,Hangup()

Outbound to Tone — the DIDs you want an agent to answer. Send only those, and leave the rest of your dial plan alone:

[from-carrier]
exten => _+919876543210,1,NoOp(Routing to Tone agent)
 same => n,Dial(PJSIP/${EXTEN}@tone-endpoint,60)
 same => n,Hangup()

Tone answers only the DIDs you imported, and only on the trunk they were imported on — anything else gets a 404. That makes it safe to experiment with routing: a mistake fails loudly rather than reaching the wrong agent.

FreePBX

The same thing through the GUI — Connectivity → Trunks → Add SIP (chan_pjsip) Trunk:

FieldValue
Trunk Nametone
Outbound — Usernameconnection.username
Outbound — Secretconnection.password
Outbound — SIP Serversip.usetone.ai
Outbound — SIP Server Port5061
Outbound — Transportthe TLS transport
Outbound — AuthenticationOutbound
Outbound — RegistrationNone
Codecsalaw, ulaw only
Advanced — Direct MediaNo
Advanced — DTMF ModeRFC 4733
Advanced — Match (Permit)Tone's signalling IPs

Then an Inbound Route per DID pointing at the trunk, and an Outbound Route for calls the agent places. Set Registration to None — FreePBX defaults to sending a registration, and Tone will not answer one.

Import your numbers

POST /v1/sip-trunks/{id}/numbers
{ "e164": "+919876543210", "agentId": "…" }

Testing

asterisk -rx "pjsip show endpoint tone-endpoint"
asterisk -rx "pjsip show aor tone-aor"

The AOR should qualify as Avail. Then POST /v1/sip-trunks/{id}/test from Tone's side, call a DID from a mobile, and finally place a POST /v1/calls.

When it does not work

What you seeWhy
Inbound from Tone lands in the wrong context[tone-identify] missing or the wrong IPs, so the call matched no endpoint.
401/407 loop on outboundoutbound_auth not set on the endpoint, or realm is not sip.usetone.ai.
Ringing then silencedirect_media is still yes — Asterisk stepped out of the media path.
DTMF ignored by the agentdtmf_mode is inband or info. It must be rfc4733.
Tone returns 403Trunk not approved yet, credentials wrong, or — with inboundAuth: "both" — your public address is not in the ipAcl.
Tone returns 404The DID was never imported, or was imported on a different trunk.
TLS handshake failsAsterisk is validating Tone's certificate against the wrong name; the peer is sip.usetone.ai.
Was this page helpful?

On this page