# Outbound routing: COR, ARS digit analysis and route patterns

> How agent-dialed outbound calls resolve: class-of-restriction checks, ARS digit analysis, ordered FRL-gated trunk preferences, digit manipulation and caller-ID selection.

The **Outbound routing (ARS)** screen configures the Avaya-model
resolution chain that TeleQuick uses for calls an agent dials from
the softphone. Instead of naming a trunk in the dial request, the agent
dials digits and the engine's outbound resolver decides which trunk
carries the call, what digits it sends, and what caller-ID it presents.

This page explains what each tab on that screen contributes to the
chain, and what happens to a live call when you change it.

> **NOTE:**
> This model applies to agent-initiated dialing. Originating a call from
> the SDK with an explicit `trunkId` bypasses ARS selection entirely.

## The resolution chain end to end

The resolver evaluates one dialed string in a fixed order:

1. **COR check** — is this agent's class of restriction allowed to place
   this call type at all?
2. **ARS digit analysis** — match the dialed string against the
   digit-analysis table to find a route pattern.
3. **Route pattern** — walk the pattern's preferences in order.
4. **FRL gating** — skip any preference whose FRL is above the agent's
   FRL.
5. **Digit manipulation and CPN** — delete leading digits, prepend an
   insert string, pick the calling-party number.
6. **Trunk** — send the INVITE on the chosen trunk.

Every tab on the screen owns one link in that chain. If a call is not
going out, work the chain in this order — a COR denial and an
unreachable preference look identical from the agent's seat.

## Classes of restriction and call types

A **class of restriction (COR)** is a named record with a `cor_code`, an
optional description, and a list of **allowed call types**. Agents are
assigned a COR on the *Agent routing* tab. A COR with no call types
listed grants nothing by that list.

The canonical call types used across the screen are:

| Call type  | Meaning                     |
| ---------- | --------------------------- |
| `local`    | Local calling               |
| `natl`     | National / domestic long distance |
| `tollfree` | Toll-free destinations      |
| `intl`     | International               |
| `svc`      | Service codes               |
| `emer`     | Emergency                   |

Call type is stored as free text on the wire, so other values are
accepted, but the list above is what the console offers and what
digit-analysis entries are tagged with. The COR check compares the call
type on the *matched digit-analysis entry* against the agent's COR, so a
destination is only as restricted as the entry that classifies it.

CORs can be toggled inactive rather than deleted.

## Digit analysis: partitions, prefixes and digit windows

Each digit-analysis entry maps a dialed-string prefix to a route
pattern. An entry carries:

| Field             | Purpose                                                             |
| ----------------- | ------------------------------------------------------------------- |
| `partition_pgn`   | Partition group number the entry belongs to.                        |
| `dialed_string`   | The prefix to match. An empty string is the catch-all.              |
| `min_digits` / `max_digits` | The digit-count window the dialed string must fall inside. |
| `call_type`       | The class checked against the agent's COR.                          |
| `route_pattern_id`| The pattern to route to on a match.                                 |
| `active`          | Inactive entries are not considered.                                |

**Matching rule:** the longest matching dialed-string prefix whose digit
count falls within `[min_digits, max_digits]` wins, and the call routes
to that entry's route pattern. Typical prefixes are the empty
catch-all, `0`, `00`, or `1`.

The console validates the numeric fields when you save. `partition_pgn`
must be between 1 and 2000; `min_digits` between 0 and 28;
`max_digits` between 1 and 28; and `max_digits` must be greater than or
equal to `min_digits`. These bounds mirror the Avaya-style limit of
28-digit dial strings.

You cannot create a digit-analysis entry before at least one route
pattern exists — the screen blocks the *New entry* action and points you
at the *Route patterns* tab.

## Route patterns and ordered trunk preferences

A **route pattern** is a named container: a `rp_code` (for example
`RP-1`, `RP-INTL`), an optional description, an `active` flag, and an
ordered list of trunk **preferences**. The route-patterns table shows
each pattern's preference count so you can spot patterns that were
created but never populated.

Each preference row holds:

| Field            | Purpose                                                    |
| ---------------- | ---------------------------------------------------------- |
| `preference_no`  | Position in the walk order. Lower numbers are tried first. |
| `trunk_ref`      | The trunk this preference sends the call out on.           |
| `frl`            | The minimum facility restriction level required to use it. |
| `del_digits`     | How many leading digits to delete before dialing.          |
| `insert_digits`  | Digits to prepend after deletion.                          |
| `cpn`            | Calling-party number override for this preference.         |
| `active`         | Inactive preferences are skipped.                          |

The resolver walks preferences top-down and takes the first one the
agent is entitled to use. Ordering is therefore the mechanism for
least-cost routing and for overflow: put the cheapest or most preferred
carrier at the lowest `preference_no`, and a fallback behind it.

Deleting a route pattern also deletes its preferences **and** any
digit-analysis entries that point at it, via foreign-key cascade. The
console confirms this before the delete. If you want to take a pattern
out of service without losing its digit analysis, clear the `active`
flag instead.

## FRL gating

Every preference carries an `frl`, and every agent carries an `frl`.
When the resolver reaches a preference, it compares the two and **skips
the preference if the agent's FRL is below the preference's FRL**. The
walk continues to the next preference.

This is how one route pattern serves agents with different
entitlements: the same dialed digits reach an expensive international
carrier for a high-FRL agent and fall through to a restricted or blocked
outcome for a low-FRL agent, with no separate digit-analysis table per
team.

Because gating happens per preference and not per pattern, an agent
whose FRL is below *every* preference in the matched pattern gets no
route at all — even though COR permitted the call type.

## Digit manipulation and caller-ID selection

Once a preference is selected, the dialed string is rewritten before the
INVITE goes out:

1. Delete `del_digits` leading digits from the dialed string.
2. Prepend `insert_digits`.

This is how you strip an access code the agent dialed, or add a carrier
prefix or country code that a particular trunk expects. Because
manipulation lives on the preference and not on the digit-analysis
entry, two preferences in the same pattern can present the same
destination in two different formats to two different carriers.

Caller-ID resolves through a fallback chain:

**agent `outbound_cpn` → preference `cpn` → trunk `outbound_cpn`**

The first value present wins. Set the agent CPN when an individual must
present their own number, the preference CPN when the presented number
must vary by carrier, and the trunk CPN as the org-wide default. The
console offers your DIDs as choices when picking a CPN, so the value you
select is a number you actually control.

## Per-agent entitlements

The *Agent routing* tab is where the chain binds to people. Each agent
row carries a display name, an email, and three routing fields:

| Field          | Effect                                                |
| -------------- | ----------------------------------------------------- |
| `frl`          | Gates which preferences in a pattern the agent may use. |
| `cor_id`       | Which class of restriction gates their call types.    |
| `outbound_cpn` | Highest-priority caller-ID for calls this agent places. |
| `active`       | Whether the agent row is in effect.                   |

An agent with no COR assigned has no call-type grant to check against,
and an agent whose FRL is lower than every preference in the matched
pattern will not route. When a single agent reports "I can't dial that
number" while the team can, these three fields are the first place to
look.

## How a change reaches the engine

Every write from this screen lands in Postgres **and** re-hydrates the
org's ARS cache key (`telequick:ars:<org>`), which the engine's
outbound resolver in `mod_sip` reads. Saves are live: you do not restart
or redeploy the engine to pick up a new digit-analysis entry, a
reordered preference, or a changed FRL.

The screen also exposes an explicit **Re-sync to engine** action in the
page header. Use it if you suspect the cached snapshot has drifted from
Postgres — for example after a direct database change made outside the
console, or after a cache flush. The re-sync rebuilds the snapshot for
the active org from the stored rows; it does not modify configuration.

Deletes propagate the same way. When you remove a digit-analysis entry,
the engine stops routing that dialed-string prefix once the snapshot is
rewritten.

## Related

- [Telemetry](/platform/telemetry) — the `trunk` label on call metrics tells you which preference actually won
- [Telephony Metrics](/glossary/metrics) — ASR and PDD per trunk, for judging preference order
