> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# What-if pricing model: rate preview over your real usage lanes

> The mental model behind the what-if pricing simulator — how your recorded usage collapses into billing lanes, how a candidate rate card re-prices them, what the lane cap does with the overflow, and where the preview ends against live rate resolution, the margin/payout previews, and the LCR route quote.

# What-if pricing — the rate preview over real usage

Pricing decisions fail on one of two axes: either the proposed rate set is wrong, or it is right on paper but wrong for **your specific traffic mix**. Devotel Orbit's what-if pricing simulator exists to close the second failure: it replays the usage events your organization already recorded through a candidate rate card and tells you what those exact messages and minutes would have cost. The traffic is held fixed; only the rates move. This page builds the model — what a usage lane is, what a candidate card is, how the two meet — so the [step-by-step guide](/guides/whatif-pricing-simulator) has an anchor and the neighboring preview surfaces stay in their own lanes.

## What the preview answers — and what it never does

The simulator answers one question: **projected spend, and the signed delta, if a candidate rate set were applied to your historical usage.** Nothing else moves:

* **It is not a traffic-mover.** A preview emits no send, touches no provider, and routes nothing. The usage it reads is already recorded; the replay is a report over your own history.
* **It is not a rater.** No charge is computed against your wallet, no invoice line appears, no pricing override changes. Applying a rate for real is a different surface — [Pricing and rate resolution](/concepts/pricing-rate-resolution) — that owns the precedence rules a live send is actually charged under.
* **It is your data, not a catalog.** The baseline comes from your own recorded usage under your own live rates, resolved with the same pricing ladder your invoice uses. A tenant with no recorded traffic gets an all-zero simulation, not an error.

That posture — compute a projection, mutate nothing — is shared with the volume-tier and margin/payout previews, and it is what makes the simulator safe to run as often as a negotiation demands.

## The usage-lane taxonomy

Everything starts from the usage records your organization accumulates as it sends — each send logs its event type, destination, direction, and quantity. A single preview call aggregates those records into **lanes** — the simulator's unit of comparison. A lane is:

```
event_type  ×  country_code  ×  direction  ×  sub_type
```

* **event\_type** maps to a billing channel: messaging usage is recorded as `<channel>_sent` (`sms_sent`, `whatsapp_sent`), voice usage as `voice_minute`. Meters a per-unit rate card cannot re-price — recording, embeddings, AI-agent conversations — are skipped entirely.
* **country\_code** is the normalized upper-case ISO alpha-2 destination, with a missing value grouping under the `*` wildcard.
* **direction** collapses inbound spellings (`mo`, `inbound`, `in`) to `mo`; everything else is `mt` (outbound).
* **sub\_type** carries finer partitions such as WhatsApp's `marketing` / `utility`; empty values group under `null`.

Quantities are summed per lane and ranked, so the largest lanes arrive first.

### The lane cap and the overflow rule

One simulation replays at most **200 distinct lanes** (`WHATIF_MAX_LANES`), cutting by descending quantity. If your traffic mix produces more lanes than that, the tail is not silently lost — it is excluded by rank, which is exactly why the guide tells you to read the lane list before the totals: lanes outside the cut never reach the response, so their cost is in neither the current nor the candidate total. A preview over a wide country mix is a projection of your **top 200 lanes**, and the totals are honest about that scope.

## Candidate rates — the partial override

The request body carries a `candidate_card`: an array of rate rows with per-lane scope. Each row pairs a lane key (`channel`, `country_code`, `direction`, `sub_type`) with a price:

* `rate_per_unit` — the wholesale base, USD per unit (per message, per voice minute). Values are USD — not cents — at this layer; the simulator converts to cents with sub-cent precision internally.
* `markup_bps` — optional proportional markup in basis points (`500` = 5%), layered on the base.
* `markup_fixed_cents` — optional flat per-unit markup in cents.
* `absolute_price_cents` — optional fixed per-unit price in cents; when set, it replaces the base-plus-markup math entirely.

The card is a **partial override**: a lane no row matches keeps its current cost untouched and reports `candidate_matched: false` with zero delta. You model only the lanes you intend to change, and the simulation total moves only for those.

When several rows could price one lane, the same most-specific-wins ladder your live rate resolution uses decides: an org-independent exact country beats the `*` wildcard; an exact sub-type beats a sub-type-agnostic row; direction must match (`mt` rows never re-price `mo` lanes). Ties resolve to the first row in array order. Because the simulator reuses the live resolver's naming and math, a candidate card you like can be expressed later as real overrides without the semantic drift two different calculators would hand you.

## Reading the response — lanes, then totals

Every preview response has the same shape: `window_days`, your echoed `label`, a `lanes` array, and a rolled-up `totals` object.

Per lane you get the quantity, both per-unit prices and both totals, the `candidate_matched` coverage flag, and the signed `delta_cents` / `delta_percent` (null when the current cost is zero but the candidate is not). All money is USD cents; unit prices carry sub-cent precision, totals are whole cents rounded up once per lane — the never-under-bill convention your invoice also uses.

The totals roll the lanes up: `current_total_cents`, `candidate_total_cents`, the signed `delta_cents` and `delta_percent`, plus two coverage counters that matter more than the delta does: `lane_count` and `matched_lane_count`. A card that matches 1 of 40 lanes gives you a projection about one lane — `delta_percent` on the totals is only meaningful once its coverage is confirmed against the lane count.

The dashboard surface — **Billing → What-if pricing simulator** — renders this same comparison interactively (a per-channel editable rate plus a global ±% slider, rolled up to one row per channel). Both the dashboard and `POST /api/v1/billing/whatif-pricing/preview` are gated to the `owner`, `admin`, or `billing` role, because the reply exposes your real per-channel spend.

## The boundary — previews never mutate billing

A what-if preview is a read with arithmetic attached. It writes no row, emits no event, moves no money, and leaves your live rates, overrides, and invoice untouched no matter what you model. A candidate rate takes effect only when it is installed as a real rate override — at which point [Pricing and rate resolution](/concepts/pricing-rate-resolution) owns the precedence and the audit trail. The simulator's job ends at the projection; resolution's job starts there.

## Which preview do I open?

Orbit ships several read-only "what would this cost" surfaces, and they answer different questions. Anchor them:

| Surface                                                                                                 | Question it answers                                                                                                                                                                                               |
| ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **What-if pricing** (`/billing/whatif-pricing/preview` — this page)                                     | "What would my recorded traffic have cost under this **rate choice**?" Re-prices your real usage lanes through a candidate card.                                                                                  |
| **Pricing previews** (`/billing/margin-rollup/preview`, `/billing/payout-ledger/preview`, and siblings) | "What is the **margin or payout** under this configuration?" Projects margin rollups, reseller revenue share, partner payouts, AI-usage charges — the operator/publisher economics layer, over inputs you supply. |
| **LCR route quote** (least-cost routing quotes)                                                         | "Which **route choice** would this send take, and what would it cost?" A route-internal, per-send ordering over upstream carriers — no traffic history involved.                                                  |

Decision box:

* Negotiating a rate card, plan, or volume tier against **your own real traffic** → what-if pricing.
* Projecting **margin or partner payout** economics → the billing pricing previews.
* Asking which **route** a specific send would take → the LCR route quote.

## Worked sample — a candidate US SMS rate against 30 days

Back-test a negotiated SMS rate for outbound US traffic against your last 30 days:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/billing/whatif-pricing/preview" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "window_days": 30,
    "label": "US SMS candidate",
    "candidate_card": [
      { "channel": "sms", "country_code": "US", "direction": "mt", "rate_per_unit": 0.0065 }
    ]
  }'
```

The replay reports each affected lane plus the roll-up; the delta fields carry the decision:

```json theme={null}
{
  "data": {
    "window_days": 30,
    "label": "US SMS candidate",
    "lanes": [
      {
        "channel": "sms",
        "country_code": "US",
        "direction": "mt",
        "sub_type": null,
        "quantity": 3420,
        "current_unit_price_cents": 0.8,
        "current_total_cents": 2736,
        "candidate_unit_price_cents": 0.65,
        "candidate_total_cents": 2223,
        "candidate_matched": true,
        "delta_cents": -513,
        "delta_percent": -18.75
      }
    ],
    "totals": {
      "current_total_cents": 2736,
      "candidate_total_cents": 2223,
      "delta_cents": -513,
      "delta_percent": -18.75,
      "lane_count": 1,
      "matched_lane_count": 1,
      "total_quantity": 3420
    }
  },
  "meta": {
    "request_id": "req_whatif_010",
    "timestamp": "2026-09-20T09:00:00Z"
  }
}
```

Read it top-down: `matched_lane_count: 1` of `lane_count: 1` says the card covered the whole scope of this window's US MT SMS lane, and `delta_cents: -513` is what the candidate rate set would have saved on that traffic — 3,420 messages re-priced from $27.36 to $22.23. A negative delta green-lights the rate; a positive one prices the trade-off before you sign.

## See also

* [What-if pricing simulator guide](/guides/whatif-pricing-simulator) — the step-by-step how-to for the dashboard and the API.
* [Pricing and rate resolution](/concepts/pricing-rate-resolution) — how a rate actually takes effect: precedence, overrides, and the path from resolution to the wallet ledger.
* [Pricing calculator](/billing/pricing-calculator) — the dashboard estimator widgets for channel, RTC, AI, and reseller payout projections.
* [Least-cost routing](/concepts/least-cost-routing) — the route-choice layer the LCR quote previews.
