Screening Contexts
Screening contexts implement destination screening — class-of-service /
toll restriction for subscriber lines. After a line’s dialed number has been
translated and a destination resolved, the screening context decides whether
that line is allowed to make the call. A call that is not permitted is
rejected with the toll-denied treatment (SIP 403, Q.850 cause 57).
Typical uses: internal-only lobby and courtesy phones, local-only residential class of service, blocking premium-rate prefixes, and time-of-day calling restrictions.
How screening fits into call processing
Section titled “How screening fits into call processing”Screening applies to line-originated calls only, and runs:
- after the translation walk and all digit manipulation — rules match the final, rewritten number, so you write them against one canonical form;
- before the destination is dialed.
Screening is not applied to:
- Trunk-originated calls — trunks are interconnects, not subscribers; use translation-context structure to control where inbound trunk traffic can go.
- Emergency calls — a number matched by a special-number profile bypasses screening entirely. A fully restricted phone always reaches 911.
- Feature codes — a matched feature code (e.g.
*98) short-circuits before translation and screening. - Calls that already failed — a no-match or forced treatment keeps its outcome; screening only gates calls that resolved to a real destination.
Configuration
Section titled “Configuration”screening-contexts is a top-level key. Each context is a map of priority
keys (numeric strings, evaluated lowest first) to rules; each rule is a
match block plus a single permit decision:
screening-contexts: local-only: "100": match: - ast-pattern: "_1XXX" # on-net extensions permit: true "200": match: - match-on-call-details: match-on: call-type match: - exact: "local" permit: true "300": match: - ast-pattern: "_+18[0245678]XXXXXXXX" # toll-free permit: trueA line profile applies a context to every call its lines originate:
line-profiles: lobby-phone: screening-context: local-only inbound-translation-context: from-lines ...An empty / omitted screening-context means no screening — the line can
dial anything its translation context routes.
Evaluation semantics
Section titled “Evaluation semantics”- Rules are evaluated in priority order, lowest first.
- The first rule whose
matchblock matches decides the call — itspermitvalue is final. Later rules are not consulted. - If no rule matches, the call is DENIED.
That last point is the defining property: a screening context is an allowlist that fails closed. You enumerate what a line may call; anything you didn’t anticipate is blocked. This is deliberate anti-fraud posture — a new premium-rate prefix or an unexpected number format is denied by default, not permitted by omission.
Explicit permit: false rules are still useful for carving exceptions out of
a broader permit:
screening-contexts: standard: # Block premium-rate before the general long-distance permit below. "100": match: - ast-pattern: "_+1900XXXXXXX" - ast-pattern: "_+1976XXXXXXX" permit: false "200": match: - regex: "^\\+1" permit: trueThe match block supports the full match engine — exact, regex,
ast-pattern, and, always/never, time-of-day, and
match-on-call-details. There
is no modify and no target: screening never rewrites the number and never
routes.
What the caller experiences
Section titled “What the caller experiences”A denied call receives the toll-denied treatment: the caller hears the
denial announcement and the call releases with SIP 403 / Q.850 cause 57.
The CDR records the treatment; the route-forensics record shows the resolved
destination and the screening denial.
Worked examples
Section titled “Worked examples”Internal-only courtesy phone (PBX)
Section titled “Internal-only courtesy phone (PBX)”Extensions and the operator, nothing else. No PSTN rule at all — everything off-net falls off the end and is denied:
screening-contexts: internal-only: "100": match: - ast-pattern: "_1XXX" - exact: "0" permit: true
line-profiles: courtesy: screening-context: internal-only inbound-translation-context: from-linesResidential local + toll-free (LEC)
Section titled “Residential local + toll-free (LEC)”screening-contexts: residential-local: "100": match: - ast-pattern: "_1XXX" # on-net permit: true "200": match: - ast-pattern: "_+18[0245678]XXXXXXXX" # toll-free families permit: true "300": match: - match-on-call-details: match-on: call-type match: - exact: "local" permit: trueLong-distance upsell is then just a profile change (screening-context:
swapped or removed) — no translation edits needed.
Business hours long-distance (enterprise)
Section titled “Business hours long-distance (enterprise)”Long-distance permitted only during staffed hours; local always permitted:
screening-contexts: hours-gated-ld: "100": match: - ast-pattern: "_1XXX" - match-on-call-details: match-on: call-type match: - exact: "local" permit: true "200": match: - and: - regex: "^\\+1" - time-of-day: days: [mon, tue, wed, thu, fri] start: "07:00" end: "18:59" timezone: America/Chicago permit: trueAfter hours, rule 200’s time window doesn’t match, nothing else covers long-distance, and the call is denied.
Premium-rate block on top of open dialing
Section titled “Premium-rate block on top of open dialing”screening-contexts: no-premium: "100": match: - ast-pattern: "_+1900XXXXXXX" - ast-pattern: "_+1976XXXXXXX" permit: false "900": match: - always: true permit: trueThe trailing always/permit: true converts the context from an allowlist
into a blocklist — use this shape only when the line class is genuinely
unrestricted apart from the enumerated exceptions.