Routes
A route block is an ordered pool of trunks with a selection strategy. Where a
trunk translation target sends a call out one specific trunk, a route
target gives the call a list of trunks to try — with failover, load
distribution, and optionally a rate-table-driven least-cost ordering in
front.
routes: domestic: strategy: sequential destinations: - trunk: carrier-a - trunk: carrier-bA translation targets it like any other resource:
target: resource-type: route resource-id: domestic failure-treatment: all-circuits-busyDestinations and strategies
Section titled “Destinations and strategies”destinations is the static trunk list. strategy controls the order the
list is tried in:
| Strategy | Ordering |
|---|---|
sequential |
Declared order, every call. Primary/backup failover. |
random |
Uniformly shuffled per call. Even load spread. |
weighted |
Random order biased by weight — higher-weight trunks are proportionally more likely to be tried first. |
With weighted, every destination must carry a positive weight:
routes: load-shared: strategy: weighted destinations: - trunk: carrier-a weight: 70 - trunk: carrier-b weight: 30Weights bias the first attempt roughly 70/30 here; the losing trunk remains in the order as failover rather than being excluded.
Failover semantics — what advances the chain
Section titled “Failover semantics — what advances the chain”Each trunk in the resolved order is dialed according to its own trunk
profile: its URI list (in uri-selection order), per-URI ring-for
duration, codec set, and egress caller-ID policy. The outcome of each
attempt decides what happens next:
- Try the next URI of the same trunk — user-level or transient outcomes where another gateway of the same carrier may still succeed: busy, temporary failure, circuit unavailable.
- Advance to the next trunk in the route — carrier- or network-level failures where retrying the same carrier is pointless: congestion, call rejected, no route to destination, network out of order, interworking errors. Exhausting all of a trunk’s URIs also advances, and so does a trunk that can’t be attempted at all — one at its concurrent-call cap, or missing/misconfigured in the running config. Listing an overflow trunk after a capacity-capped primary is therefore a supported pattern: calls spill to the next destination the moment the primary is full.
- Stop immediately with a treatment — permanent, number-level outcomes that every carrier would reject the same way: unallocated number, invalid number format, number changed. Failing over would only delay the error the caller is going to hear anyway. A genuine no-answer likewise ends the attempt rather than re-ringing the same called party via another carrier.
When the whole chain is exhausted, the caller receives the route target’s
failure-treatment (default reorder).
These outcomes are visible per-call: the CDR’s egress leg records which trunk ultimately carried the call, and the disposition and Q.850 cause of the final attempt.
The LCR engine
Section titled “The LCR engine”An lcr block puts a rate table in front of the static destination list.
At call time the switch looks up candidate trunks in the table, ranks them by
effective cost, and tries them before destinations:
routes: domestic-lcr: strategy: sequential lcr: tablename: us-rates prefix-match-source: dialednum prefix-match-logic: prefix selector: "!all" route-count: 3 assumed-call-duration-seconds: 120 destinations: - trunk: overflow-carrierRate tables themselves — rows, uploads, versioning, activation — are managed through the LCR API and covered in the LCR deck administration guide. The route block only points at a table and sets the lookup policy.
Block reference
Section titled “Block reference”| Field | Values | Meaning |
|---|---|---|
tablename |
string (required) | Which of your LCR tables to consult. The table’s currently active version is used. |
route-count |
positive integer (required) | Maximum LCR candidates to try (top-N by effective cost). |
prefix-match-logic |
exact | prefix |
exact: single point lookup of the whole key. prefix: longest-prefix match. |
prefix-match-source |
dialednum or a call-detail field |
What value keys the lookup — the dialed number, or an enrichment field such as lrn or dlata. |
selector |
literal | !all | !match-on:<field> |
Which rows within a matched prefix are eligible (below). |
assumed-call-duration-seconds |
positive integer, default 60 | The duration used in the effective-cost comparison. |
How a lookup runs
Section titled “How a lookup runs”-
Resolve the lookup key from
prefix-match-source.dialednumuses the post-translation dialed number; a call-detail field (e.g.lrn) resolves from enrichment data. A leading+on the key is stripped before matching, so an E.164 dialed number or LRN compares digits-to-digits against the table — store your table prefixes as bare digits (1305, not+1305). -
Match the prefix. With
prefixlogic, the longest prefix present in the table that matches the key wins — a1415555row beats1415beats1. Withexact, only a row keyed by the full value matches. -
Filter by selector. Each table row carries a selector column:
- a literal selector matches rows whose column equals it — use this to keep several independent rate decks in one table;
!allaccepts every row under the prefix;!match-on:<field>resolves the named call-detail field at call time and matches rows whose selector equals its value — e.g. rows tagged percall-typejurisdiction.
-
Filter by validity window. Rows carry optional effective-on / valid-until timestamps; rows outside their window at call time are ignored. This is how future rate decks are staged in advance.
-
Rank by effective cost. Each row has a per-minute cost, a setup cost, and a priority. Candidates are ordered by:
effective-cost = setup-cost + per-minute-cost × (assumed-call-duration-seconds / 60)Lower cost first; among equal costs, lower priority value first; exact ties are broken randomly per call, spreading traffic across equivalent carriers. Costs are integer units — only relative magnitudes matter, so pick one unit (e.g. thousandths of a cent per minute) and use it consistently within a table.
-
Truncate to
route-countand dial in order, with the same failover semantics as static destinations. The staticdestinationslist follows as the final fallback.
When LCR is skipped
Section titled “When LCR is skipped”The lookup contributes nothing — and the call falls through to the static
destinations list — when:
- the named table has no active version,
- no prefix in the table matches the key,
- the
selectoris!match-on:<field>and the field can’t be resolved for this call (enrichment data unavailable — the usual fail-closed behavior), prefix-match-sourcenames a call-detail field that can’t be resolved,- a candidate row references a trunk that no longer exists in your configuration (that row is skipped; the rest are kept).
Activating a new table version propagates to call processing within about 30 seconds.
Worked examples
Section titled “Worked examples”Primary / backup failover
Section titled “Primary / backup failover”routes: simple-failover: strategy: sequential destinations: - trunk: primary-carrier - trunk: backup-carrierJurisdiction-aware LCR (ITSP)
Section titled “Jurisdiction-aware LCR (ITSP)”One table, rows tagged with a call-type selector, port-corrected lookups:
routes: rated-domestic: strategy: sequential lcr: tablename: us-rates prefix-match-source: lrn # rate where the number lives, not its dialed prefix prefix-match-logic: prefix selector: "!match-on:call-type" # match rows tagged local / interstateInterlata / ... route-count: 3 assumed-call-duration-seconds: 180 destinations: - trunk: overflow-carrierA ported number rates by its LRN; the jurisdiction selector picks the
correct rate deck; the three cheapest valid carriers are tried in order;
overflow-carrier catches everything the table can’t.
Weighted commit management (LEC)
Section titled “Weighted commit management (LEC)”Meet a minimum-commit on carrier A while keeping carrier B warm:
routes: commit-split: strategy: weighted destinations: - trunk: carrier-a weight: 85 - trunk: carrier-b weight: 15Verifying route behavior
Section titled “Verifying route behavior”The route-forensics record shows the translation walk that selected the route; the CDR’s egress leg shows which trunk finally carried the call, the dialed URI, and the final disposition. When validating a new LCR table or selector policy, the LCR API’s lookup/resolve preview endpoints let you run the same resolution the call path uses against a hypothetical call — see the LCR deck administration guide.