Skip to main content

Least-cost routing (LCR) policy

For every outbound SMS there are two selection problems: which sender the message goes out from, and which upstream route carries it to the destination operator. Sender resolution owns the first. The LCR policy owns the second — your per-organization preference for ordering Devotel’s wholesale trunk against your own connected (BYO) carriers. This page covers what the policy controls, how routes are scored, and the dry-run quote you can run before saving a change. Routing behaviour only — the request and response schemas live in the API reference.

Where LCR sits in the routing chain

LCR evaluates recipients on three inputs — the recipient’s identifier class, the destination it implies, and the per-route price — and runs after sender resolution:
  1. Sender resolution picks the sending identity (a sender pool, an explicit from, a messaging-service default, or the fallback chain).
  2. LCR upstream selection — the stage this page covers — ranks the wholesale trunk and your BYO carriers for that destination.
  3. Delivery proceeds over the top-ranked route; route health and circuit breakers then meter every route continuously (see Outbound route quality and circuit breakers).
Sender pages describe which sender is picked; LCR describes which carrier path that sender’s traffic then takes. Every candidate stays within Devotel’s wholesale softswitch and your own BYO carriers — outbound SMS never leaves that boundary.

How routes are scored

Each BYO carrier you have connected for a destination is scored on four axes, multiplied together: The highest composite score is the route the live gateway prefers. Your policy decides how that scorer-first order relates to the Devotel wholesale entry:
  • trafficType: "cost" — rank BYO carriers strictly by the cost axis.
  • trafficType: "quality" (default) — rank BYO carriers by the composite score.
  • preferByo: false (default) — the Devotel wholesale trunk leads; BYO carriers follow in scorer order.
  • preferByo: true — every active BYO carrier ranks before the Devotel entry, without you enumerating them.
  • candidateOrder — an explicit list of candidate ids (the reserved id devotel, and BYO carrier ids) that completely overrides the editorial order. Unknown or stale ids are dropped at evaluation time, and unnamed candidates fold back into scorer order — a misconfigured list degrades, it never errors.

The dry-run quote preview

You don’t have to commit a policy to see what it would do. The quote endpoint ranks routes for a destination and returns the full breakdown:
  • Request: a destination in E.164 (to), an optional message quantity (drives per-segment totals), and an optional policy body. With no policy, your saved policy is used; pass one to preview a candidate policy before saving it.
  • Response: winnerId / winnerKind, the policy actually in force, an enabled flag, and a candidates array in rank order. Each candidate carries rank (1 = winner), a plain-language reason (for example “Devotel first (preferByo: false)” or “BYO before Devotel (preferByo)”), estimatedUnitCents / estimatedTotalCents for the Devotel entry, and compositeScore for BYO entries.
Cost figures are always honest: the Devotel row carries the ledger pricing-ladder values, and a BYO row returns null cents because your upstream spend with that carrier isn’t computable — a fabricated 0 would mislead the preview. GET /messaging/lcr/policy returns the saved policy (or the defaults on absence), and PUT /messaging/lcr/policy upserts yours idempotently.

Common configuration patterns

  • Cheapest-first — set trafficType: "cost" with preferByo: true. Your lowest-priority BYO carriers lead, and the Devotel trunk becomes the runner-up rather than the fallback default.
  • Filtered to a region — scope carriers per-country on the carrier profile (matching by destination MCC list). The quote only considers carriers whose country scope matches the destination, and the Devotel entry is priced for the resolved destination country.
  • Sticky incumbents — keep the default quality mode: the route a message last succeeded on keeps a small advantage until its health degrades, so near-equal scores don’t cause route flapping.

Auditability

Every policy write is recorded: each PUT /messaging/lcr/policy call writes an audit entry naming the enabled flag, traffic type, BYO preference, and candidate order, so a route-ordering change is always attributable to a user and a timestamp. Reads also surface the effective policy even when no policy has been saved (the wire defaults).

See also