Skip to content

Call-Detail Enrichment

Most routing decisions match on the dialed digits. Call-detail enrichment lets you match on what the numbers are: the operating company (OCN) that owns them, their LATA, state, and rate center, the ported-number routing number (LRN), and the call’s overall jurisdiction. This makes carrier-aware and jurisdiction-aware routing a first-class part of the configuration — no external rating engine required.

Typical uses:

  • Send calls destined for a specific carrier’s numbers down a direct interconnect trunk instead of your default route.
  • Apply interstate vs. intrastate rate decks by matching the call’s jurisdiction.
  • Route by the LRN rather than the dialed number, so ported numbers rate and route according to where they actually live.
  • Screen or scrub caller ID based on the calling number’s state or rate center.

Enrichment data is available for three numbers, selected by the first letter of the field name:

Prefix Number Description
s… Source (calling party) The presented caller ID
d… Destination (called party) The dialed number
l… Routing number (LRN) Where the called number actually routes after porting lookups

The LRN (Location Routing Number) is the key to correct handling of ported numbers: a number that looks like it belongs to one carrier and region may have been ported elsewhere. When port data is unavailable for a call, the routing-number fields fall back to the called number’s own data.

These field names are accepted everywhere a call-detail field can be used:

Field Meaning Example value
lrn Routing number in E.164 form +12015550100
socn / docn / locn OCN (Operating Company Number) of the calling / called / routing number 9206, 624H
slata / dlata / llata LATA of the calling / called / routing number 224
sstate / dstate / lstate US state of the calling / called / routing number NJ
sratecenter / dratecenter / lratecenter Rate center of the calling / called / routing number JERSEYCITY
call-type The call’s jurisdiction classification interstateInterlata

The call as a whole is classified into exactly one of:

Value Meaning
local Local calling area
intrastateIntralata Same state, same LATA (not local)
intrastateInterlata Same state, LATA boundary crossed
interstateInterlata State boundary crossed
international Outside the NANP, or NANP country boundary crossed

Match these strings exactly — they are case-sensitive.

match-on-call-details is a match operation, usable in any match list — translation contexts, caller-ID contexts, and screening contexts. It names a field with match-on, and carries a nested match list that is evaluated against the field’s value instead of the dialed number:

match:
- match-on-call-details:
match-on: docn
match:
- exact: "9206"
- exact: "624H"

The nested list supports the same operations as any match block (exact, regex, ast-pattern, and, …), OR’d together. Combine a call-detail condition with a digit pattern using and:

match:
- and:
- ast-pattern: "_+1NXXNXXXXXX"
- match-on-call-details:
match-on: call-type
match:
- exact: "interstateInterlata"
  • Lazy. The telephony-data queries run only when a call actually reaches a rule that references a call-detail field (or an explicit collect-numberinfo, below).
  • Cached per call. The first lookup’s result is reused for every subsequent call-detail match on the same call — many rules can reference these fields without repeated queries.
  • Fail-closed. If data cannot be resolved for a call — an unparseable or anonymous caller ID, a number outside the coverage data engine (currently only NANP), or a lookup failure — the match evaluates false. The call is never matched by guesswork; it simply falls through to your later rules.

Controlling when enrichment is captured: collect-numberinfo

Section titled “Controlling when enrichment is captured: collect-numberinfo”

Enrichment results are also stamped onto the call’s detail records (CDRs) for rating and reporting. By default that happens only if a matching rule needed the data. A modify block can force the lookup at a specific point in the walk with collect-numberinfo: true:

"100":
match:
- always: true
modify:
operations:
- strip-prefix: "9"
- must-normalize: e164
collect-numberinfo: true

The lookup runs after the block’s rewrite operations, so you can place it where the number is canonical — access codes stripped, normalized to E.164 — and the CDR enrichment reflects the real destination rather than the raw dialed string. The captured data is recorded on the inbound leg and every outbound leg the routing decision produces.

Least-cost-routing lookups can also be driven by call-detail fields, in two places on a route’s lcr block:

  • prefix-match-source — which value keys the rate-table prefix lookup: the literal dialednum, or any field above. Using lrn here makes your rate lookups port-corrected.
  • selector — which rows are eligible within a matched prefix: a literal value, !all, or !match-on:<field> to resolve a field at call time and match rows whose selector column equals it.
routes:
domestic-lcr:
strategy: sequential
lcr:
tablename: us-rates
prefix-match-source: lrn # rate the routing number, not the dialed number
prefix-match-logic: prefix # longest-prefix match
selector: "!match-on:call-type" # pick the row matching this call's jurisdiction
route-count: 3
destinations: # fallback if no LCR row hits
- trunk: default-carrier

With rate rows uploaded per jurisdiction (selector values local, intrastateIntralata, interstateInterlata, …), the same table applies the correct rate deck automatically per call.

You have a direct trunk to a neighboring carrier and want their numbers to bypass your default LCR route. Match the routing number’s OCN so ported numbers go the right way:

translations:
wholesale-out:
# Traffic homed on the neighbor's switches → direct interconnect.
"100":
match:
- match-on-call-details:
match-on: locn
match:
- exact: "9206"
target:
resource-type: trunk
resource-id: neighbor-direct
failure-treatment: all-circuits-busy
# Everything else → normal LCR.
"200":
match:
- always: true
target:
resource-type: route
resource-id: domestic-lcr

If the OCN lookup fails for a call, rule 100 simply doesn’t match and the call takes the LCR route — degraded, but never stuck.

Different carriers for interstate and intrastate traffic:

translations:
rated-out:
"100":
match:
- match-on-call-details:
match-on: call-type
match:
- exact: "interstateInterlata"
target:
resource-type: route
resource-id: interstate-carriers
"200":
match:
- match-on-call-details:
match-on: call-type
match:
- exact: "intrastateIntralata"
- exact: "intrastateInterlata"
- exact: "local"
target:
resource-type: route
resource-id: intrastate-carriers
# Data unavailable or international → safe default.
"900":
match:
- always: true
target:
resource-type: route
resource-id: default-carriers

A screening context that permits local and in-state calls but blocks out-of-state toll calling from restricted lines. Screening rules are evaluated lowest priority first; the first match decides, and no match means deny:

screening-contexts:
in-state-only:
"100":
match:
- ast-pattern: "_[2-9]XX" # extensions / short codes
permit: true
"200":
match:
- match-on-call-details:
match-on: call-type
match:
- exact: "local"
- exact: "intrastateIntralata"
- exact: "intrastateInterlata"
permit: true
# Anything else (interstate, international, unresolvable) is denied
# by the fail-closed default -- no explicit deny rule needed.

A line profile references this with screening-context: in-state-only; blocked calls receive the toll-denied treatment (SIP 403). Note that the fail-closed lookup works for you here: a call whose jurisdiction can’t be determined is denied, not permitted.

Caller-ID contexts share the same match engine, so a trunk’s outbound-caller-id-context can brand the presented number differently by the caller’s state:

caller-id-contexts:
brand-by-state:
"100":
match:
- match-on-call-details:
match-on: sstate
match:
- exact: "NJ"
- exact: "NY"
modify:
operations:
- replace: "+12015550100" # regional main number
"200":
match:
- always: true
modify:
operations:
- replace: "+18005550100" # national number for everyone else

Rate and route on where numbers actually live:

routes:
ported-aware:
strategy: sequential
lcr:
tablename: onnet-rates
prefix-match-source: lrn
prefix-match-logic: prefix
selector: "!all"
route-count: 2
destinations:
- trunk: overflow-carrier

A dialed number in the 212 NPA that has been ported to a carrier you interconnect with directly will match your on-net rate rows by its LRN — even though its dialed prefix suggests otherwise.

Every routing decision is captured in the route-forensics log, including each call-detail rule evaluated and whether it matched. When validating a new jurisdiction- or carrier-based rule, place a few test calls and pull their forensic records: the trace shows the exact rule-by-rule walk, so you can confirm the call-detail match fired (or see that data was unavailable and the call fell through) without guessing from the final destination alone.