Inbound message routing
Inbound message routing is the tenant-level rule engine that decides where a received (MO) message goes. Each rule tests one field of the incoming message and points at one destination. The first enabled rule that matches, in ascending priority order, wins; a message that matches nothing follows the same default path it always has. This page explains the routing model and when to use it. Request and response schemas for the rule endpoints live in the Messaging API reference; the sibling concept How routing picks a sender covers the outbound side of the same decision.Why inbound routing exists
Without rules, every inbound message on a number follows one fixed path: the per-DID inbound hook you configure on that number. That hook is a single destination per number, so it cannot express “when the body contains STOP, don’t touch my application endpoint” or “messages to the support line go to the support team, but only when they mention a booking.” Anything conditional had to live in your own webhook receiver. Inbound routing rules move that conditional logic into the platform. A rule set can describe cases like:- anything containing the keyword
appointmentgoes to the scheduling webhook; - anything from
+441632960000lands in the VIP inbox; - everything else is left to the default handling.
How a message flows through the rules
- Priority is the ordering tool, not rule names or creation order. A lower
priorityvalue evaluates first. Leave gaps between values (10, 20, 30) so you can insert a rule between two existing ones without reordering everything. - Specific rules need lower priority values than general ones. If a broad keyword rule sits at priority 10 and a VIP-sender rule at priority 20, the broad rule consumes messages the VIP rule was meant to catch. Put the exceptions first.
Match types: what a rule can test
Each rule inspects exactly one field of the inbound message, selected bymatchType, and compares it to matchValue:
Three properties of the matcher are worth knowing before you build on it:
- Keyword is a substring match, not a word match. A rule on keyword
bookalso matches “booking” and “rebooked”. Use aregexrule with word boundaries (for example\bbook\b) when you need whole-word matching. - Regex rules are validated twice. A syntactically invalid pattern is rejected with a 422 when you create the rule, and a pattern over 512 characters is refused. If a stored pattern somehow fails to compile at runtime, that rule simply never matches — it does not break evaluation of the rest of the rule set.
- Equality is normalised.
numberandsendercomparisons strip whitespace and dashes and ignore case, so+1 415-555-0100in a rule matches the E.164 form the carrier delivers.
Target types: where a matched message goes
targetType selects the destination; targetConfig carries its parameters.
Webhook targets
A webhook rule proves itself useful when the destination infrastructure is yours: your own receiver gets the routed message as a JSON POST with the sender, destination number, body, and the matched rule id. Three guarantees apply to every webhook dispatch:- HTTPS only. Rule creation rejects
httpURLs outright. - SSRF-guarded. URLs that resolve to private, loopback, or link-local addresses are rejected both at rule-write time and again at dispatch time, so a rule cannot be used to make the platform call internal addresses.
- Signed when a secret is set. If you provide
targetConfig.secret(minimum 8 characters), every dispatch carries anX-Orbit-Signature: sha256=<hex>header — an HMAC-SHA256 of the exact request body, keyed by your secret — so your receiver can verify provenance. Secrets are write-once: they are masked in list and get responses, and you rotate one by replacing it.
Inbox destinations: queue, inbox, team
These targets keep the conversation inside Orbit’s inbox product. A match publishes an assignment for the matched conversation to the named queue, inbox, or team, so the message lands with the right group of agents without passing through your infrastructure. Use these when the response to the message is a human replying from the dashboard.SMS menus: the ivr target
An ivr rule turns a matched inbound message into the start of an interactive SMS session — the messaging analogue of a voice IVR. targetConfig.menu carries the tree: a root node plus named nodes, each with a prompt and options keyed by what the sender texts back (1, yes, and so on). An option can send a reply, descend into another node, or both.
Sessions are stateful across messages with a sliding 30-minute TTL: the sender’s next text continues the traversal rather than re-matching the rule set, and someone who walks away mid-menu silently drops out. The menu is validated at rule-write time — dangling branches, duplicate option keys, and oversize prompts fail creation with a 422 instead of failing live. Keep prompts inside 1600 characters (the 10-segment SMS cap used across the platform).
Replies the menu sends are ordinary outbound SMS and exit through the same routing any other outbound message uses.
One-shot replies: auto_reply and appointment
These targets answer the inbound message themselves. auto_reply sends one templated reply, with an optional cooldown so a contact sending ten keyword messages gets one response, not ten. appointment classifies a reply against your confirm/reschedule/cancel keyword sets and sends the matching acknowledgement — enough for self-service appointment handling without a webhook round-trip.
Failure tracking and the fall-through path
Every rule carries the same operational fields as the inbound-email routing feature: a consecutivefailureCount, a lastMatchedAt timestamp, and a lastFailureAt timestamp. A successful dispatch resets the failure count to zero; a failed dispatch (a non-2xx response or a network error on a webhook target) increments it and stamps lastFailureAt. These counters are built for health badges and auto-pause behaviour on the dashboard: read them to spot a webhook endpoint that has been failing for a week.
The no-match path is deliberately unglamorous. When no enabled rule matches — or when the whole rule evaluation itself fails, because routing must never eat a message — the inbound message does exactly what it did before any rules existed: it persists to the inbox and fires the tenant-wide received webhook event. Rules are an overlay on that default, never a replacement for it. A tenant with zero rules pays zero overhead: the engine short-circuits before any of the session or matching logic runs.
What inbound routing never does
Inbound routing is strictly inbound-side. It can match a received message and hand it to a destination; it cannot originate an outbound message. No match type and no target type will ever send a message on your behalf — targets either hand the conversation to the inbox layer or POST to your own endpoint, and even the reply-style targets (ivr, auto_reply, appointment) only reply to a message that already arrived, through the shared outbound sender-resolution path.
If you need to send a message that is not a reply — a campaign, a reminder, a notification — that is the outbound send API, with its own sender-resolution rules documented in How routing picks a sender. Inbound rules are never part of that path.
Create a keyword-to-webhook rule
Creating a rule is one POST. This one routes any inbound message whose body contains the keywordappointment to your HTTPS endpoint, signs each dispatch, and evaluates before the default-priority rules:
smsr_… id for later PATCH and DELETE calls. Creating, updating, and deleting rules requires the owner or admin role; listing is likewise restricted. For the full field list and the update/delete shapes, see the Messaging API reference.
See also
- Inbound message resolution — the tenant-ownership resolution layer (number index, routed states, hourly reconcile, per-DID override) underneath the rule engine
- How routing picks a sender — outbound sender resolution and the sticky-reply side of inbound rules
- Send and receive messages — end-to-end walkthrough
- Messaging API reference — request and response schemas for the inbound-routes endpoints