Smart routing: preview a channel before you send
POST /api/v1/messages/route-preview runs the smart router without sending anything. You pass the recipient and the message shape; it returns the channel the router would pick, a plain-language reason, the ordered fallback chain, a per-message cost estimate, and a predicted engagement score. Use it to plan campaigns, validate a channel choice before you pace a send, and sanity-check fallback behavior against real recipients — all without spending a credit or creating a message row.
The same scoring logic runs on the live send path (channel: "auto" on POST /messages, and POST /messages/smart-send), so what the preview returns is what dispatch would do with the same inputs.
When to preview vs. send
Preview when the answer changes what you send or how you pace it:- Planning a campaign. Before you commit an audience to a channel, run the preview against a sample of recipients. If the recommendation is not the channel you assumed, adjust the audience or the capability flags — not the campaign after launch.
- Validating channel-cost before pacing.
estimatedCostCentsis priced through the same channel cost table the router scores with, so a preview loop over a recipient cohort gives you a per-channel cost picture for pacing decisions (throttle, credit caps) before the first dispatch. - Fallback sanity checks. Toggle the capability flags (
whatsapp_available,rcs_available, …) to confirm the router degrades the way you expect — e.g. that an RCS-first marketing blast lands on WhatsApp or SMS rather than voice when RCS is off. - Picking a send shape. If
channel: "auto"outperforms your fixed channel on engagement for a cohort, switch the campaign to auto-routing before you schedule it.
Endpoint shape
POST /api/v1/messages/route-preview — read-scoped, tenant-keyed rate limit at the standard read budget.
Your API key needs at most the messages:read scope (a write key works too). Create or edit keys in the dashboard under Settings → API keys.
Request
All capability flags default to absent (not
false) — an absent flag means “unknown,” which excludes the channel from the candidate set.
Response — 200
Read the response, field by field
channel— the router’s pick. One ofsms,whatsapp,email,viber,rcs,push,voice. This is the channel a dispatch with the same inputs would use.reason— a plain-text explanation of why it won, composed from whichever factors fired:cost-optimized(cost-optimize is on and the pick is cheap),high engagement (N%)(the pick’s engagement meets the 70% threshold),fast delivery for urgent message,rich content for marketing. When nothing distinctive fired, the reason states the pick scored highest for that recipient and message type. Two special cases: OTP messages return the regulatory SMS rule verbatim, and a recipient with no detected capabilities returns"No channel capabilities detected — defaulting to SMS".fallbackandfallbackChain—fallbackis the next channel to try;fallbackChainis the full ordered list after the recommended channel, in priority order. The first entry always equalsfallback. Wire the chain into your send logic so a hard failure on the primary hops tofallbackChain[0], then[1], and so on. An empty array means no other channel is reachable for that recipient — treat the primary pick as terminal.estimatedCostCents— per-message cost in cents on the recommended channel, before any volume tiering. Sub-cent channels (email, push at 0.1¢) come back as fractional values, so multiply rather than round when you sum a cohort. Multiply by your cohort size for a planning total.engagementScore— predicted engagement probability, 0.0–1.0. This is yourengagement_ratesvalue for the recommended channel when supplied, otherwise the platform default for that channel. Use it to compare channel strategies across a cohort — a marketing blast where auto-routing averages 0.7 and your fixed channel averages 0.45 is a signal to switch.
Wire the preview into a send pipeline
The pattern: prefetch recipient capabilities, preview, then dispatch with the router’s chain as your fallback ladder.@devotel/sdk-node):
- Trust the chain, not your own ladder. The returned
fallbackChainalready encodes capability and cost ordering. Hand-rolled ladders skip that math. - Org-level fallback still applies. If your tenant has cross-channel fallback rules configured (Settings → Channels → Cross-Channel Fallback), transparent retry on terminal delivery failures continues below your chain — see Messaging Best Practices.
Preview interactively: Outbound → Route Preview
The dashboard surfaces the same endpoint as an operator tool, so a non-API teammate can answer “what would Orbit do here?” without writing code. Navigate to Outbound → Route Preview. Fill in the recipient (E.164 phone), optionally an email and country, pick the message type and urgency, toggle the recipient’s reachable channels and the cost-optimize bias, then select Preview route. The result shows the recommended channel with its reason, the fallback order as a pill chain, the per-message estimate in your account currency, and an engagement bar. Owner, admin, and developer roles can run a preview; viewers cannot — matching the API’s read-scope posture. Nothing is sent from this screen either. Use the dashboard for one-off validation (“does enabling cost-optimize swap SMS for WhatsApp on this Brazil recipient?”); use the API when the preview runs inside a pipeline.route-preview vs. the composite risk verdict
The two pre-send lookups answer different questions — use both on high-cost traffic, in this order:
Run
risk/score first on unfamiliar or high-cost destinations: a high-band verdict is a reason to refuse the destination regardless of what the router recommends. For destinations you accept, route-preview settles the channel. Both are read-only lookups — see the SMS pumping protection guide for the risk verdict and the cut-offs behind its bands.
Failure modes and safety properties
- Read-scoped keys work. A
messages:readkey can call the endpoint; you do not need to hand a planning job a write key. - Nothing ever sends. The endpoint runs the scoring function and returns — it writes no message row, touches no wallet, and emits no webhook. Repeatedly calling it in a planning loop is safe and cheap.
- No cross-tenant leakage. Responses are computed from the request body plus platform defaults. When you omit
engagement_rates, the defaults are global per-channel constants — the preview never reads another tenant’s data, and it returns your own historical rates only when you supply them. 422 VALIDATION_ERROR. A malformed body (missingto,body,message_type, orurgency; bad enum value; engagement rate outside 0–1) fails schema validation before any routing runs. Validation errors are reported once per field with a human-readable message — fix the named field and retry. The fix path does not need a retry loop.- All-capabilities-off is not an error. A recipient with no flagged capabilities and no email gets a 200 with
channel: "sms"and the diagnostic reason"No channel capabilities detected — defaulting to SMS". Decide in your pipeline whether that fallback suits the recipient — it is the router’s last-resort default, not a delivery guarantee. - Rate limit. The route sits behind the tenant-keyed read budget (per-tenant, per-minute). A planning pass over a large cohort should still pace calls or batch over seconds — a
429returnsretry_afterand is safe to retry after that window.
The preview is deterministic — same inputs, same recommendation. Change the capability flags, urgency, message type, or
cost_optimize to see how the router’s answer moves; the response’s reason tells you which factor fired.