Skip to main content

Inbound routing

Inbound routing answers two questions in order: which tenant owns this call, and which destination inside that tenant.

Which tenant

Matched in this order, first match wins:

  1. a global inbound_overrides entry;
  2. the owning tenant of the trunk, if the trunk names one;
  3. a tenant's exact DID claim (dids.numbers);
  4. a tenant's DID pattern (dids.patterns);
  5. a global catch-all override (did: "*").

If nothing matches, the call is rejected.

configs/tenants/acme/tenant.yaml
dids:
numbers: ["+31201234567"]
patterns: ['^\+312012345\d{2}$']

A number claimed by two tenants is a validation error, so the ordering between steps 3 and 4 only matters when an exact number falls inside another tenant's pattern — the exact claim wins.

Which destination

Once the tenant is known, its own inbound_routes decide where the call goes:

configs/tenants/acme/tenant.yaml
inbound_routes:
- did: "+31201234567"
destination:
type: extension
target: "200"

- did: "+31201234568"
destination:
type: ring_group
target: sales
FieldValues
didThe number as it arrives, or * for a catch-all inside the tenant.
destination.typeextension or ring_group
destination.targetThe extension number, or the ring group name.

Routes are matched in order. A tenant with a single number usually needs one route; a tenant with a block usually adds a * catch-all at the end so an unmapped number still lands somewhere:

inbound_routes:
- did: "+31201234567"
destination: {type: extension, target: "200"}
- did: "*"
destination: {type: extension, target: "999"} # everything else

Global overrides

inbound_overrides in lyno.yaml win over everything a tenant claims. This is how a number is redirected without touching the tenant that owns it:

configs/lyno.yaml
inbound_overrides:
# hand acme's number to globex's extension 900
- did: "+31201234567"
tenant: globex
destination: {type: extension, target: "900"}

# only reassign the tenant; globex's own inbound routes then decide
- did: "+31201234599"
tenant: globex

# catch-all for numbers no tenant claims
- did: "*"
tenant: acme
destination: {type: extension, target: "999"}
FieldRequiredMeaning
didyesThe number, or * as a global catch-all.
tenantyesThe tenant that will handle the call.
destinationnoPins the destination. Without it, the tenant's own inbound routes run.

An override is the right tool for temporary redirects — porting a number, covering an outage, moving a customer — because it is one entry in one file and leaves both tenants' configuration intact.

Worked example

Two tenants, one shared carrier:

configs/lyno.yaml
trunks:
- name: carrier
host: sip.provider.example

inbound_overrides:
- did: "+31201234567"
tenant: globex
destination: {type: extension, target: "900"}
configs/tenants/acme/tenant.yaml
name: acme
domains: [acme.pbx.example.com]
dids:
numbers: ["+31201234567", "+31201234568"]
inbound_routes:
- did: "*"
destination: {type: extension, target: "200"}

A call to +31201234568 reaches acme extension 200 through the catch-all. A call to +31201234567 — which acme also claims — is taken by the override and lands on globex extension 900 instead. Nothing in acme's file changed.