Skip to main content

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

  1. the calling tenant's outbound_routes, in order;
  2. the global outbound_routes in lyno.yaml, in order;
  3. 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

configs/lyno.yaml
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
FieldRequiredMeaning
nameyesLabel, used in logs and error messages.
patternyesGo regular expression matched against the dialled number.
stripnoDigits removed from the front of the number.
prependnoText put in front of what remains.
trunkyesThe 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.

Order beats cleverness

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:

configs/tenants/acme/tenant.yaml
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_trunk is unset.