Skip to content

Translation Contexts

Translation contexts are the core routing engine of the switch. Every call that enters your namespace — from a subscriber line, from a SIP trunk, or handed over from another namespace — is walked through a translation context, which decides three things:

  1. Match — does this rule apply to the call?
  2. Modify — how should the dialed number be rewritten?
  3. Target — where does the call go?

If you have worked with a class-4/5 switch before, a translation context is a digit-analysis table; if you come from a PBX background, it is a dialplan context.

Contexts are named blocks under the top-level translations: key. They never run on their own — something must point a call at them:

Referenced from Field Applies to
Line profile inbound-translation-context Every number a line dials
Trunk profile inbound-translation-context Calls arriving on the trunk (DID delivery)
Trunk profile outbound-translation-context Calls being sent out the trunk
Namespace linkage inbound-transfer-context / outbound-transfer-context Calls crossing between namespaces
Special-number profile translation-context Early-matched emergency/priority numbers (911)
Voicemail context outbound-translation-context The message-callback (“return the call”) feature
Another translation target: translation-context Chained contexts

A context is a map of priority keys (numeric strings) to translation objects. Each object has an optional match, modify, and target block:

translations:
from-lines:
"100":
match:
- ast-pattern: "_1NXXNXXXXXX"
modify:
operations:
- add-prefix: "+"
target:
resource-type: route
resource-id: outbound-lcr
"200":
match:
- always: true
target:
resource-type: treatment
resource-id: cannot-complete-as-dialed

Use spaced priorities ("100", "200", "300") so you can insert rules later without renumbering. Keys must be integers in quotes; non-integer keys are ignored.

The rules are evaluated lowest priority first. For each rule:

  1. The match block is evaluated against the current dialed number. Entries in the list are OR’d — any one matching makes the rule match. A rule with no match block never matches (use always: true for a catch-all).
  2. On a match, the modify operations run in order, rewriting the number.
  3. If the rule has a target, the walk ends — the call goes to that destination. First matching rule with a target wins.
  4. If the rule has no target (a rewrite-only rule), the walk continues from the next rule, carrying the rewritten number forward. The walk never restarts from the top, so a rewrite-only always rule cannot loop.
  5. If no rule matches by the end of the chain, the caller receives the reorder treatment.

Every walk is recorded in the route forensics log: each rule evaluated, whether it matched, every rewrite step with before/after values, and the final target. When a call routes somewhere unexpected, the forensic record shows you exactly which rule fired.

Each entry in a match list is one operation. Top-level entries are OR’d; use and to require several conditions together.

Literal string equality against the current number.

match:
- exact: "911"

Quote the value — YAML would otherwise read a bare number and can drop leading zeros.

An Asterisk-style dialplan pattern. Patterns must start with _; without the underscore the value is treated as a literal (same as exact).

Symbol Matches
X any digit 0–9
Z any digit 1–9
N any digit 2–9
[125-8] one character from the set (ranges with -)
. one or more remaining characters (must be last)
! zero or more remaining characters (must be last)
- ignored (readability separator)
match:
- ast-pattern: "_1NXXNXXXXXX" # 11-digit NANP
- ast-pattern: "_011." # international with 011 prefix

A regular expression (RE2 syntax). The expression matches anywhere in the number unless you anchor it — almost always anchor with ^ and $:

match:
- regex: "^\\+1415555\\d{4}$"

A nested list of match operations that must all be true. This is how you combine a number pattern with a time window or a call-detail condition:

match:
- and:
- ast-pattern: "_NXXNXXXXXX"
- time-of-day:
days: [mon, tue, wed, thu, fri]
start: "08:00"
end: "17:59"
timezone: America/Chicago

and lists can nest further and blocks, patterns, time-of-day, and match-on-call-details entries.

Matches on the wall-clock time the call is processed.

Field Form Empty means
days list of mon tue wed thu fri sat sun every day
start "HH:MM" 24-hour from midnight
end "HH:MM" 24-hour, inclusive through end of day
timezone IANA name, e.g. America/New_York UTC

Matches on enriched telephony metadata about the calling, called, or routing number (OCN, LATA, state, rate center, jurisdiction) instead of the dialed digits. Covered in full on its own page: Call-Detail Enrichment.

match:
- match-on-call-details:
match-on: call-type
match:
- exact: "international"

always: true matches unconditionally — the standard catch-all / default rule. never: true never matches — useful for temporarily disabling a rule without deleting it.

The modify block rewrites the dialed number. Operations run in listed order, each seeing the previous operation’s output.

modify:
failure-treatment: cannot-complete-as-dialed
operations:
- strip-prefix: "9"
- must-normalize: e164
Operation Argument Effect
replace string Replace the entire number with the argument
normalize / must-normalize e164, nanp, or national Reformat the number (see below)
strip-prefix / must-strip-prefix string Remove the argument from the front if present
strip-suffix / must-strip-suffix string Remove the argument from the end if present
add-prefix string Prepend the argument
add-suffix string Append the argument
isolate-digits "start:stop" Keep only digits, then keep the inclusive zero-based range
regex-replace / must-regex-replace "pattern!replacement" Regex substitution (see below)

Normalize modes. The number is parsed as a phone number (10-digit numbers are assumed to be North American):

  • e164 — full international form: +13125551212
  • nanp — bare 10-digit national number: 3125551212. Fails for non-NANP (non-+1) numbers.
  • national — the number’s national display format.

isolate-digits. First removes every non-digit character, then keeps the inclusive, zero-based range start:stop. Either side may be empty: "0:2" keeps the first three digits, "3:" drops the first three, ":" keeps all digits (i.e. just strips formatting).

regex-replace. The argument is split on the first ! into a regex and a replacement; the replacement may use capture groups ($1, $2). If the pattern does not match, the number is left unchanged (or, for the must- variant, the rule fails):

# 011 + country code dialing → E.164
- regex-replace: "^011(\\d+)$!+$1"

Every conditional operation comes in two flavors:

  • Soft (strip-prefix, normalize, …): if the operation cannot apply (prefix absent, number unparseable, regex didn’t match), it is skipped and the walk continues normally.
  • must- (must-strip-prefix, must-normalize, …): if the operation cannot apply, the rule fails terminally — the caller immediately receives the block’s failure-treatment (default reorder).

The target block names the destination. resource-id may contain the token !dialedNum, which is substituted with the dialed number after the modify block has run — this is how one rule serves a whole number block.

target:
resource-type: line
resource-id: "!dialedNum"
not-found-treatment: vacant-code
failure-treatment: temporary-failure
resource-type resource-id The call…
line line name or !dialedNum rings the line’s devices (honoring the line’s features)
ring-group ring-group name runs the hunt group
device device name rings a single device directly, bypassing line features
trunk trunk name is sent out one specific SIP trunk
route route name is sent out a route block (trunk selection strategy and/or LCR)
voicemail-box box name or !dialedNum goes straight to the mailbox to leave a message
treatment treatment name plays the treatment announcement/tone and releases (see below)
recording recording name plays the named recording
namespace namespace ID or granted alias is transferred into another namespace, subject to that namespace’s linkage policy
pstn is transferred to the PSTN via your granted pstn alias
hangup is released immediately with normal clearing
translation-context context name or !dialedNum continues in another translation context
application application name is handed to an application (e.g. voicemail.access, voicemail.access-menu)

Two per-target treatment overrides are available on every target:

  • not-found-treatment — played when the referenced resource doesn’t exist in the running configuration. Default: reorder.
  • failure-treatment — played when the destination action fails (for example, the dial failed and no fallback answered). Default: reorder.

A translation-context target jumps the walk into another context, carrying the (rewritten) number with it. This is useful for factoring routing into stages — e.g. an access-code stage, then a shared routing stage used by both lines and inbound transfers.

Chains are limited to 8 hops, and the validator rejects configurations whose contexts form a loop.

A treatment plays an announcement or tone to the caller and releases the call with a specific cause. Your PBX or upstream switch sees these as SIP final responses; they also appear in your call detail records as the Q.850 cause.

Treatment Meaning Q.850 SIP
reorder congestion / routing failure (fast busy) 34 503
busy called party busy 17 486
disconnect normal clearing 16
vacant-code number not in service 1 404
unallocated-number number not assigned 1 404
no-route no route to destination 3 404
cannot-complete-as-dialed invalid number format 28 484
temporary-failure transient network failure 41 503
all-circuits-busy concurrent-call cap reached 34 503
denied call rejected by policy 21 403
toll-denied destination not permitted for this line (toll restriction) 57 403
unauthorized-code unauthorized access/feature code 21 403
caller-not-accepted caller rejected 21 403
not-acceptable rejected by policy 21 403
spam rejected as suspected spam 21 403

Four-digit extension dialing, an operator, night service, and PSTN access — the context every line’s profile points at:

translations:
from-lines:
# Extension dialing: 1000-1999 → the matching line.
"100":
match:
- ast-pattern: "_1XXX"
target:
resource-type: line
resource-id: "!dialedNum"
not-found-treatment: vacant-code
# Operator: business hours → front-desk ring group…
"200":
match:
- and:
- exact: "0"
- time-of-day:
days: [mon, tue, wed, thu, fri]
start: "08:00"
end: "17:59"
timezone: America/New_York
target:
resource-type: ring-group
resource-id: front-desk
# …after hours → general-delivery voicemail.
"210":
match:
- exact: "0"
target:
resource-type: voicemail-box
resource-id: "100"
# Rewrite-only: normalize any NANP form to E.164, then continue.
"300":
match:
- ast-pattern: "_NXXNXXXXXX"
- ast-pattern: "_1NXXNXXXXXX"
modify:
operations:
- must-normalize: e164
# Off-net: everything E.164 goes to the PSTN.
"400":
match:
- regex: "^\\+"
target:
resource-type: pstn
failure-treatment: temporary-failure
# Catch-all.
"900":
match:
- always: true
target:
resource-type: treatment
resource-id: cannot-complete-as-dialed

Because forwards re-originate from the line, a line with call-forward-always set to a mobile number walks this same context — and succeeds, because rules 300/400 handle off-net numbers. Without them, forwarding would break.

Inbound calls from a carrier arrive on a trunk; the trunk profile’s inbound-translation-context delivers DIDs to lines:

translations:
from-carrier:
# Normalize the incoming DID to 10 digits.
"100":
match:
- always: true
modify:
failure-treatment: cannot-complete-as-dialed
operations:
- must-normalize: nanp
# Main number → auto-attendant ring group.
"200":
match:
- exact: "4155551000"
target:
resource-type: ring-group
resource-id: main-line
# DID block 415-555-1001..1099 → matching lines.
"300":
match:
- ast-pattern: "_41555510XX"
target:
resource-type: line
resource-id: "!dialedNum"
not-found-treatment: vacant-code
# Unknown DID: end here — NEVER route back out the trunk.
"900":
match:
- always: true
target:
resource-type: treatment
resource-id: unallocated-number

An outbound context that gates international calling, normalizes everything to E.164, and hands domestic traffic to an LCR-backed route:

translations:
wholesale-out:
# 011 international dialing → E.164, only if the account allows it.
"100":
match:
- ast-pattern: "_011."
modify:
failure-treatment: cannot-complete-as-dialed
operations:
- must-regex-replace: "^011(\\d+)$!+$1"
target:
resource-type: route
resource-id: international-carriers
failure-treatment: no-route
# Domestic: normalize and send to the LCR route.
"200":
match:
- ast-pattern: "_NXXNXXXXXX"
- ast-pattern: "_1NXXNXXXXXX"
modify:
failure-treatment: cannot-complete-as-dialed
operations:
- must-normalize: e164
target:
resource-type: route
resource-id: domestic-lcr
failure-treatment: all-circuits-busy
"900":
match:
- always: true
target:
resource-type: treatment
resource-id: cannot-complete-as-dialed

For routing on jurisdiction, carrier of record, or ported-number data (for example, sending traffic for a specific OCN down a direct interconnect), see Call-Detail Enrichment.