/api/v1/voice/dnis-routes
Authentication: Clerk session (Authorization: Bearer <token>) or API key (X-API-Key).
Scope: voice:write for create, replace, and delete (owner, admin, or developer role); reads require a signed-in session or API key.
1. How matching works
Each routing rule is a tuple:pattern_kind—prefixorregex.pattern— the match expression. Forprefix, an E.164 prefix such as+4420matches every dialed number that starts with it. Forregex, a regular expression the dialed E.164 number must match in full, e.g.\+1(800|888|877)\d{7}for the classic US toll-free ranges. Patterns are stored and evaluated in E.164 form, so include the leading+.name— a label for the grid and audit log, e.g. “Support EN toll-free pool”.priority— an integer, lower wins. Rules at priority 100 (the default) are considered before rules at priority 500; a priority-10 carve-out beats both.target_type+target_config— where a matched call goes. The target types are the same set inbound routing uses:queue,ivr,agent,voicemail,sip_forward,transfer,conference,ring_group,shared_line,softphone_user,softphone_register,dispatch_to_voicemail_box, ordecline. The config shape is whatever that target type takes — the same JSON body you’d put on a matching inbound route, e.g.{ "queueName": "Support_EN" }.active— flip off to disable a rule without deleting it. Inbound calls it would have matched fall through to lower rules or your default handling.
- A per-number override wins first. If the dialed DID has its own entry in inbound routing, that entry handles the call — no pattern is consulted. This is how you carve out one special number from a block (see section 5).
- Among DNIS rules, the server evaluates your rules in precedence order: lowest
prioritynumber first; within the same priority, the most specific pattern (longest pattern string) first; ties broken by the oldest rule. The first rule that matches the dialed number handles the call. - If no rule matches, the call falls through to your organization’s default inbound handling.
- Specific beats general only within the same priority. A broad prefix like
+1at priority 10 still loses to a narrow prefix like+1800555at priority 10, but+1at priority 10 beats+1800555at priority 100. Keep broad catch-alls at a high priority number (e.g. 900) so narrow rules can sit below them. - Overlap is rejected, not silently tolerated. Creating or replacing a rule whose pattern overlaps another active rule at the same priority returns
409— ambiguous routing is an error, not a coin flip. Layer the rules on different priorities to express “broad fallback, narrow override.”
active: false) are never evaluated.
2. Manage rules from the dashboard
Open Voice → DNIS pattern routing (/voice/dnis-routes) in the dashboard. The page is a grid of your rules, already sorted in match-precedence order — the order the server evaluates them in.
What you see in the grid: one row per rule with Name (your label), Pattern (monospaced, with a prefix or regex badge next to it), Target (the target type, e.g. queue), Priority (the integer), and Status (Active or Inactive badge). The rightmost column carries an edit pencil and a delete button on each row. Above the grid, a filter box narrows the rows by name, pattern, or target — useful once a pool grows past a screen of numbers. When you have no rules yet, the page shows an empty state with a Create rule button.
To create a rule:
- Click Create rule (top right).
- In the dialog, set Name, Pattern kind (
E.164 prefixorRegex), Priority (default 100, lower wins), and the Pattern itself — prefix example+1800, regex example\+1(800|888|877)\d{7}. - Choose the Target type and fill Target config (JSON) with that target’s settings, e.g.
{ "queueName": "Support_EN" }. The same shape you’d use on the matching inbound-route type. Leave it{}for targets that need no config. - Leave Active on, click Create rule.
+ followed by digits, a regex must compile, the config must parse as a JSON object, and priority must be a whole number. Invalid fields show an inline message and block submit — so you fix them before any API round-trip.
To edit or delete: use the pencil (opens the same dialog prefilled — a save replaces the rule) or the trash icon (asks for confirmation first, because numbers matched only by that rule fall through to your default handling on delete). Deleting shows the rule’s name in the confirmation so you delete the right one.
3. Manage rules over the API
The same CRUD the dashboard uses is public:
Create a rule that sends every UK geographic London number you own to the support queue:
cURL
id.
pattern_kind—prefixorregex(required).pattern— E.164-form prefix (leading+, digits) or a full-match regular expression (required, max 256 chars).name— operator label shown in the dashboard grid (required, max 128 chars).priority— integer 0–100000, default 100; lower wins at match time.target_type— one of the inbound target types listed in section 1 (required).target_config— the target’s settings as a JSON object;{}when the target needs none.active—trueby default; setfalseto stage a rule without enabling it.
PUT /api/v1/voice/dnis-routes/{id} sends the same body and replaces the rule in full. DELETE /api/v1/voice/dnis-routes/{id} returns 204 No Content.
Responses you’ll plan for:
422— a field failed validation (patternempty,priorityout of range, malformed enum). Thedetailsobject names the offending fields.409— the pattern overlaps an existing active rule at the same priority. Change the priority to layer the rules, or edit the existing rule instead.404— the rule id doesn’t exist for your organization (wrong id, or another tenant’s id).401/403— missing credentials, or the caller lacks an owner/admin/developer role or thevoice:writescope on a write.
4. Test the rule with a sample call
Before pointing a production number block at a rule:- Create the rule with
active: false, or with the real pattern but a harmless target such asvoicemail. - Confirm the match order with
GET /api/v1/voice/dnis-routes/— your rule should appear where you expect in the precedence ordering (server-sorted: priority ascending, then most specific within the priority). - Buy or pick one DID in the block and call it from a PSTN phone. The call should land on the target — the queue rings its members, the IVR plays its opening prompt, or the voicemail greeting answers.
- If the call hits your default handling instead, check in order: does the DID’s pattern actually match (E.164 form, leading
+included)? Is the ruleactive? Is a higher-priority or more-specific rule matching first? Does the DID itself have a per-number inbound routing entry overriding the pattern? - When the sample behaves, flip
activetotrue(or point the target at the real queue) — every DID in the block now routes the same way.
5. Common setups
One queue behind N country DIDs. Buy local numbers per region, then one prefix rule per region pointing at the same queue:+1 at priority 900 sends every otherwise-unmatched US/Canada DID to a general IVR, while narrower rules at priority 100–500 handle the pools you’ve actually mapped. The catch-all only ever sees calls no specific rule claimed.
6. Troubleshooting
Next steps: Build an IVR flow for the
ivr target, Set up and run voice queues for the queue target, and Queue SLA escalation policies for watching what the routed calls do to your SLA.