Outbound routing
When a dialled number is not a local extension, outbound routes decide which trunk carries it and what number is actually sent.
The order
- the calling tenant's
outbound_routes, in order; - the global
outbound_routesinlyno.yaml, in order; default_trunk, with the number unchanged.
The first pattern that matches wins — within a list and across the fallback. If
nothing matches and there is no default_trunk, the call fails.
A route
outbound_routes:
- name: emergency
pattern: '^112$'
trunk: carrier
- name: national
pattern: '^0[1-9]\d{8}$'
strip: 1
prepend: "+31"
trunk: carrier
- name: international
pattern: '^\+\d+$'
trunk: carrier
default_trunk: carrier
| Field | Required | Meaning |
|---|---|---|
name | yes | Label, used in logs and error messages. |
pattern | yes | Go regular expression matched against the dialled number. |
strip | no | Digits removed from the front of the number. |
prepend | no | Text put in front of what remains. |
trunk | yes | The trunk to use. Must exist. |
Manipulation is applied in that order: match, then strip, then prepend. In the
national example, 0201234567 matches, loses its leading 0, and is sent as
+31201234567.
Routes are tried top to bottom, so put the specific ones first. ^112$ before
^\d+$ is the difference between reaching emergency services and reaching your
carrier's error announcement.
Per-tenant routes
A tenant's routes are tried before the global ones, which is how one tenant gets its own carrier for a subset of numbers without affecting anybody else:
outbound_routes:
- name: acme-mobile
pattern: '^06\d{8}$'
strip: 1
prepend: "+31"
trunk: acme-mobile-carrier
Everything acme dials that is not a Dutch mobile falls through to the global routes as usual.
Caller ID
The number presented is the calling tenant's caller_id, falling back to
the trunk's caller_id. That is what allows several tenants to share one
carrier account and still each present their own number.
Calling an external number from a dialplan
Inside a dialplan, an external destination is written external:<number>, and
that number goes through exactly the routing described above:
- id: follow-me
type: dial
target:
kind: followme
stages:
- targets: ["101", "external:0612345678"]
timeout: 25s
or as a direct target:
- id: forward
type: dial
target:
kind: external
number: "+31612345678"
Debugging a route
- Run with
-debug: the matched route name and the resulting number are logged when a call is placed. - A call that leaves on the wrong trunk is almost always an earlier route
matching more broadly than intended — check anchoring (
^and$). - A call that fails with no trunk means nothing matched and
default_trunkis unset.