> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing rules: prefix-based provider resolution

> Manage the platform routing rules table: create, patch, test, and delete the prefix-based rules that resolve which provider handles SMS and Voice traffic, understand how matching ranks rules, and know when to use a routing rule versus your LCR policy.

# Routing rules: prefix-based provider resolution

Routing rules are the platform table that decides which provider handles SMS and Voice traffic, matched on the destination number's prefix. Every call or message consults the same resolver: drop the wildcard rules, take the longest matching prefix first, break ties on the lowest priority number. Create a rule, and traffic steering applies on the next call or message — there is nothing to deploy.

Operators manage this table in the admin console at **Routing** (`/dashboard/routing`): create, edit, toggle, and delete rules, filter the table by channel, and run a test resolution before saving anything. This page walks that workflow end to end.

## How prefix matching ranks rules

Rules are `channel` × `direction` × `prefix` rows, and each one pins a `provider`. Only **active** rules participate in resolution. For a destination like `+14155550123`:

1. The resolver considers active rules for that channel and direction.
2. A rule matches when its `prefix` is the literal `*` wildcard, or a leading prefix of the destination number.
3. Matching rules rank wildcard-last, then by longest prefix, then by lowest `priority` number. The first row wins.

A worked example — say these outbound SMS rules are active:

| Prefix  | Provider     | Priority | Matches `+1415...`? | Rank                                              |
| ------- | ------------ | -------- | ------------------- | ------------------------------------------------- |
| `+1415` | `provider-a` | 1        | yes                 | 1 — longest prefix wins                           |
| `+1`    | `provider-b` | 1        | yes                 | 2 — shorter prefix                                |
| `*`     | `provider-c` | 1        | yes                 | last — wildcards sort after every specific prefix |

Provider A takes the call. Delete or deactivate that rule and Provider B takes over immediately; the wildcard keeps the destination routable at all times. That is also the safe staging pattern: add a specific prefix, test it, and remove the catch-all only when the specific route is proven.

Two more behaviors to know before you write rules:

* **Sender-based resolution for SMS.** For outbound SMS, the sender identity resolves before destination rules run: an alphanumeric sender ID routes over Devotel's A2P (SMPP) routes outright, and an E.164 sender falls back to the prefix match above. Test both shapes — the console's test panel offers both.
* **Platform-wide SMS override.** A platform-wide override can force every outbound SMS through one provider (`Force Devotel SMPP` or `Force Telnyx`), and when it is active it wins over every SMS rule regardless of priority. The routing page shows a warning banner while that override is set; Voice rules are unaffected.

## Create a rule

In the console, use **Add Rule** and set the fields. Over the API, `POST /admin/routing-rules` accepts the same body:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/admin/routing-rules" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "direction": "outbound",
    "prefix": "+1415",
    "provider": "provider-a",
    "priority": 1,
    "active": true
  }'
```

* `channel` is `sms` or `voice`; `direction` is `outbound` (default) or `inbound`.
* `prefix` is required — a leading-prefix string such as `+1415`, or `*` for the wildcard.
* `priority` is a positive integer, default `1`. It only breaks ties between equal-length prefixes, so reach for a more specific prefix before you reach for a priority bump.
* `active` defaults `true`. A malformed body — an unknown channel, an empty prefix, a non-integer priority — fails the request with a 422 and changes nothing.

Every create, update, and delete writes a routed audit entry (`routing_rule.created`, `routing_rule.updated`, `routing_rule.deleted`) with the operator and timestamp, so a routing change is always attributable. The list endpoint is paginated (`limit`/`offset`, 100 rules per page by default, 500 maximum) — page through it instead of assuming one response holds the whole table.

## Patch a rule

`PUT /admin/routing-rules/:id` takes a partial body — send only the fields you are changing:

```bash theme={null}
curl -X PUT "https://api.orbit.devotel.io/api/v1/admin/routing-rules/rr_1w2x" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "active": false }'
```

Toggle `active` to stage a rule without deleting it: deactivated rules stop matching immediately but stay in the table, which makes a rule cheap to roll back. An unknown rule id returns a 404. In the console, the row's edit action and the active toggle wrap these same calls.

## Test before you save

Never blind-write a route. The test endpoint runs the same resolver the platform uses, against the rule set as it stands, and reports the winner — nothing sends and nothing bills:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/admin/routing-rules/test" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms", "direction": "outbound", "phone_number": "+14155550123" }'
```

```json theme={null}
{
  "data": {
    "matched": true,
    "phone_number": "+14155550123",
    "channel": "sms",
    "direction": "outbound",
    "provider": "provider-a",
    "rule_id": "rr_1w2x",
    "prefix": "+1415",
    "priority": 1
  }
}
```

A `matched: false` response with `provider: null` means no active rule reached the number — usually a missing wildcard. For SMS sender steering, `POST /admin/routing-rules/test-sender` takes `{ "channel", "sender" }` instead: an alphanumeric sender reports the SMPP resolution, an E.164 sender reports its prefix match. In the console, **Test Route** runs the destination flavor from the page header.

## Delete a rule

`DELETE /admin/routing-rules/:id` removes the rule and returns `204`; an unknown id returns a 404. Rules that have been replaced by a longer-prefix rule are the right things to delete — stale short-prefix duplicates invite a future priority bump to silently flip the winner. Keep the wildcard until you are sure a specific rule covers every destination you send to.

## Circuit-breaker interplay

Routing rules and circuit breakers compose: the rule picks the provider, the breaker decides whether that route is healthy enough to carry traffic right now. A rule that pins a destination to a breaching provider does not strand the traffic — the route-quality layer auto-suspends a sustained-failing route and fails traffic over until it recovers. The full monitor-and-breaker workflow (scores, thresholds, opt-in enforcement) is documented in [Configure route-quality thresholds and run the circuit breaker](/guides/route-quality-circuit-breaker-runbook); nothing on this page duplicates it.

## Routing rule or LCR policy

Rules and the LCR policy answer different questions, and the order matters:

* A **routing rule** is a directive — "destination numbers with this prefix go to this provider." Use rules for hard routing decisions: carve a prefix out to a specific provider, keep a compliance-required route fixed, pin a country to one network. Rules are platform-level and operate on exact prefix matches.
* The **LCR policy** ranks candidates *within* the route a rule pointed at — cheapest versus best-quality first, Devotel's wholesale trunk before or after your own connected BYO carriers, or an explicit candidate order. Use the policy when the question is cost or delivery quality, not "which provider."

If you find yourself encoding cost preferences into rule priorities, you have reached the wrong tool — that is what [Configure your LCR policy](/guides/configure-lcr-policy) is for, and its dry-run quote previews the ranked order before you commit. Conversely, a prefix that must always reach one provider no matter the prices is a routing rule, full stop.

## See also

* [Configure your LCR policy (Devotel-first vs BYO-first)](/guides/configure-lcr-policy) — rank candidates on cost or quality once the rule picks the provider
* [Configure route-quality thresholds and run the circuit breaker](/guides/route-quality-circuit-breaker-runbook) — auto-suspend a breaching route the rules selected
* [Least-cost routing (LCR) policy](/concepts/least-cost-routing) — the scoring model behind the policy
* [How routing picks a sender — and where a reply lands](/concepts/sender-and-routing) — where rules and LCR sit in the outbound chain
* [Smart routing: preview and validate a channel before you send](/guides/smart-route-preview) — preview multi-channel routing without sending
