Skip to main content

Configure your LCR policy (Devotel-first vs BYO-first)

The least-cost-routing (LCR) policy is your per-organization preference for ordering outbound SMS routes: Devotel’s wholesale trunk first, or your own connected (BYO) carriers first. This guide walks the whole workflow — attach a BYO carrier, read the current policy, preview the ranked route order without saving anything, and commit the change. The model behind the scoring lives on the LCR concept page; this page is the task.

When this applies

Configure an LCR policy when all three of these are true:
  • You send SMS in volume, so route order moves real money or real delivery rates.
  • You have your own upstream carrier relationship(s) — an SMPP account you can hand Orbit as a BYO carrier — or you’re about to attach one.
  • You want to steer the route order (cheapest BYO first, BYO-before-Devotel, or an explicit order) instead of the out-of-the-box default.
If you only ever send over the Devotel wholesale trunk, the default policy already does the right thing; skip the rest of this page.

Decision table

Four fields make a policy. Every field is optional on a write; omitted fields fall back to the defaults shown. The safe starting point — and the platform default — is trafficType: "quality" with preferByo: false. Devotel’s trunk leads, and your BYO carriers follow in quality order; a degraded BYO bind never outranks the healthy trunk on price alone. Flip to "cost" only when per-message margin on your own carrier contract genuinely outweighs delivery quality for your destinations, and verify that with the preview before saving.

Read the current policy

The response wraps the policy in the standard data envelope:
You never have to guess what a never-configured organization returns: when no policy has been saved, the read rebuilds the wire defaults above and returns those — the read never errors and never returns 404.

Write the policy

PUT upserts the policy idempotently — re-sending the same body is a no-op — and writes an audit entry (lcr_policy.updated) with the effective values, so a route-order change is always attributable to a user and a timestamp.
To pin an explicit route order, send candidateOrder with the reserved devotel id plus your BYO carrier ids (the id values returned when you register the carriers — see Attach a BYO carrier):
The service validates the whole body: a bad enum (trafficType: "cheapest"), a non-boolean preferByo, or a 422-inducing shape fails with a 422 and changes nothing.

Preview before you commit

You don’t have to save a policy to see what it would rank. The dry-run quote takes a destination, an optional message quantity, and optionally a policy body; with no policy it evaluates your saved policy, and with one it evaluates the request-scoped body so nothing commits.
The response ranks every candidate and names the winner:
Read the numbers honestly:
  • The Devotel entry carries a real estimatedUnitCents and estimatedTotalCents off the same pricing ladder the ledger settles; priceSource names where the price came from (rate_card, an override, or the default lane).
  • A BYO entry always shows null cents, because what you pay your own upstream for that destination isn’t computable here — a fabricated 0 would mislead the comparison. The compositeScore is the BYO entry’s competitive figure instead: cost × delivery quality × bind health × sticky bonus, multiplied (see How routes are scored).
  • winnerId / winnerKind marks the route the live gate would pick under the evaluated policy, and every candidate carries its integer rank and a plain-language reason you can show in a console.
A dry run bills nothing and moves no traffic.

Attach a BYO carrier

Your BYO carrier is the upstream account Orbit binds to over SMPP. Register it once, keep it healthy, and the LCR candidates update on their own.
  1. Register the carrier with POST /api/v1/messaging/smpp/carriers — the bind coordinates (remoteHost, remotePort, remoteSystemId, remotePassword, bindType), a memorable label, a scope ("all", or "by_country_mcc" with scopeFilter.mccs to limit it to specific destination operator lists), and a priority. Worked requests, in cURL and SDK form, live in the SMPP API reference; the bind lifecycle is covered in Connect via SMPP.
  2. Give it a cost figure. priority is a monotonically decreasing cost score: 0 maps to a full 1.0 cost score, the default 100 to 0.5, and 300 to 0.25 — set it intentionally; a cost-mode policy ranks on exactly this number.
  3. Keep the bind healthy. A failed SMPP bind nearly removes the carrier from contention; an unbound one is demoted over a bound peer; and a bind that hasn’t been heard from for a while takes a smaller, partial hit. The dashboard renders the breakdown under Developer → SMPP → Route scores, and GET /api/v1/messaging/smpp/carriers/route-scores reads the same composite over the API.
If your quote skips a carrier entirely, check the scope: a "by_country_mcc" carrier only enters the candidate set when the destination resolves to one of the MCCs in scopeFilter.mccs.

Cut over

Change a live policy the way you change any route-shaping config — stage, check, then flip:
  1. Stage explicitly. Set the candidate list you intend (candidateOrder), not a naked preferByo flip where the BYO family rank surprises you. Preview it with POST /messaging/lcr/quote and confirm the winnerId on a handful of your real destinations.
  2. Save. PUT /messaging/lcr/policy with the staged body.
  3. Watch failover. Rank order isn’t the whole story — a lead route that degrades hands traffic to the next ranked route. Track that with Outbound route quality and circuit breakers and the runbook at Configure route-quality thresholds. If the cutover hurts delivery, flip preferByo back to false (or restore your saved candidateOrder) — a one-call rollback.

See also