1. What inbound routes do
A rule is amatch condition → destination pair plus a priority number:
- Match on one of four things.
number(the receiving number, normalized E.164 equality),sender(the originating number, same equality),keyword(case-insensitive anywhere in the body), orregex(case-insensitive pattern on the body, compiled when you save it — a pattern that does not compile is rejected with400instead of silently never matching). - Send the match to a destination. Your own webhook (
url, optional HMAC signature withX-Orbit-Signature: sha256=<hex>), a queue, a shared inbox, a team, a templated auto-reply, a stateful SMS menu (SMS-IVR), or an appointment self-service flow (confirm / reschedule / cancel). - First match wins. Rules evaluate in ascending priority; ties go to the older rule. No match means the message falls through to default delivery — your inbox still gets it.
2. Access
The surface is gated to the owner/admin roles — same permission tier as inbound email routing. Colleagues with lower roles cannot list, create, update, or delete rules in the dashboard or through the API.3. Where it lives
Messages → Settings → Inbound Routes in the dashboard (/messages/settings/inbound-routes). The API surface sits under /api/v1/messages/inbound-routes.
4. Define a rule step by step
- Open Create rule. Give the rule a name — something that reads well in an audit log later (“Support keyword → helpdesk”, not “Rule 3”).
- Set the match. Pick
number,sender,keyword, orregex, then enter the value. Everything is checked on save: a payload that breaks the field contract answers422from the API, aregexpattern that does not compile answers400, and the dashboard shows the same rejection inline instead of silently never matching. - Pick the destination. Webhook needs an HTTPS, publicly reachable URL. For queue/inbox/team, paste the destination’s id. Auto-reply takes the reply text, with
{{from}},{{keyword}}, and{{body}}tokens. - Set priority. Lower numbers evaluate first — leave gaps (10, 20, 30) so you can insert a rule between two neighbors later.
- Verify with a sample inbound. Send a matching text from a handset, then check the rule row:
Last matched atstamps on a hit, and a failing webhook destination accumulates afailuresbadge. - Order, pause, remove. Priority is the only ordering knob — move the number to re-sequence. The per-rule switch pauses without deleting; fully remove with the row’s delete action, after which the message falls back to default delivery.
5. API parity — CRUD over cURL
All four verbs live under/api/v1/messages/inbound-routes; live keys are prefixed dv_live_sk_:
priority, ties broken by creation time — the same order the matcher applies them.
Create a rule — 201:
id in the 201 body is the handle for the PATCH and DELETE calls below.
Update a rule — partial, only the fields you send change:
200 { "ok": true } confirms the write; re-list to read the rule back with "enabled": false. Re-ordering works the same way: PATCH priority to the new number and the next list reflects the new evaluation order.
Delete a rule — defaults take over again:
404 NOT_FOUND with the same envelope.
Rejected writes — the two validation shapes:
A payload that breaks the field contract answers 422 VALIDATION_ERROR and names the offending field in details.issues (here priority above its 0–100000 range):
regex rule whose pattern does not compile is rejected on save (as promised in the walkthrough above) with 400 VALIDATION_ERROR:
secret on a webhook rule, every delivery signs the exact JSON body with HMAC-SHA256 and sends the digest as X-Orbit-Signature: sha256=<hex>. Create the signed rule like any other:
secret is write-once — list readbacks mask it as ••••, as the list response above shows. A matched inbound then arrives at your URL as this POST, with the signature header over the raw body:
failureCount, visible in the list response.
SDK escape hatch — Node and Python:
Inbound routes sit outside the typed helper scope, so drive them through each SDK’s low-level request method. It inherits your API key, 429/5xx retry, and the shared error tree — the hatch contract is documented in Use the core-scope SDK escape hatch.
- Node
- Python
name (≤120 chars), matchType, matchValue (≤512 chars), targetType, targetConfig (shape depends on the destination — { url } for webhook, { queueId | inboxId | teamId }, { template } for auto-reply), priority (0–100000, default 100), enabled, and description. A webhook secret (8–256 chars) is write-once: readbacks mask it as ••••.
6. When nothing matches
An unmatched inbound goes to the shared inbox exactly as if no rules existed — the rules list never blocks delivery. To see what a rule did, use its counters: a hit stampsLast matched at; a webhook destination that keeps failing shows its failure count on the rule row. The message itself remains readable from the Delivery log and your subscribed webhooks, the same as for unrouted traffic.
7. Worked example set
Keyword opt-out routing. RouteSTOP-style vocabulary to a helpdesk reviewer while built-in opt-out handling still records consent — short codes and brand-specific words stay tenant-owned controls. Match keyword = OPTOUT, target your team’s inbox or queue. (The built-in STOP/opt-in keywords are handled before any rule runs; see keyword auto-reply rules for the consent-recording path.)
VIP sender to a priority queue. Match sender = the customer’s handset in E.164, target a queue named vip-support. Both number and sender compare normalized: spaces and dashes collapse before the equality check.
Short-code triage with regex. A regex like ^(APPT|BOOK)\b matched against the body can route a family’s appointment keywords to the appointment self-service destination, or to the SMS menu for an interactive prompt tree. Anchored patterns stay faster — the pattern source is capped at 512 characters.
Fallback for unrecognized regions. Keep your dashboard’s defaults underneath: put a low-priority catch-all at priority: 90+, and let anything it doesn’t claim fall back to the shared inbox. Operators then see the applied rule stamped on the rule row for every message that matched, and default delivery everywhere else.
See also
- Inbound number routing — the per-DID choice a rule stacks underneath
- Keyword auto-reply rules — consent-recording keyword handling before this engine
- Keyword rules worked examples — match-type expectations and precedence
- Inbound email routing — the email analogue of this surface
- Verify webhook signatures — verifying the
X-Orbit-Signatureheader a signed webhook destination receives - Use the core-scope SDK escape hatch — the
client.requestcontract the Node and Python samples here rely on - Delivery log — where routed and unrouted messages land
- Opt-out lists — the consent records keyword rules write