Skip to main content

What-if pricing simulator

Orbit’s what-if pricing simulator answers one operator question: what would this rate card have cost on the traffic I already sent. You pick a look-back window, model a candidate rate card, and the simulator replays your own recorded usage through it — per channel, destination country, direction, and message sub-type — and returns the exact cost delta versus what you paid under your live rates. The traffic is held fixed; only the rates change. Use it to back-test a negotiated sheet, a new plan, or a competitor’s published rates before you commit. Two ways in, same engine:
  • DashboardBilling → What-if pricing simulator (/billing/simulator) is the interactive surface: pick a window, drag a global ±% adjustment, or type a per-channel rate, and read the projected spend.
  • APIPOST /api/v1/billing/whatif-pricing/preview is the programmatic path when you need per-country, per-direction, or per-sub-type granularity, or want to run comparisons from your own tooling.
Both require the owner, admin, or billing role — the same gate as the other billing surfaces, because the reply carries your real per-channel spend.

1. What the simulator does

A preview run has three steps, all read-only:
  1. Aggregate your usage. The usage events your own organization recorded over the look-back window (1–90 days) are grouped into billing lanes: channel × country code × direction (mt outbound, mo inbound) × sub-type, with quantities summed per lane.
  2. Price the baseline. Each lane is priced under your current live rates with the same pricing resolver that produces your invoice — the number on the bill and the number in the simulator come from the same math.
  3. Replay the candidate. Every lane is re-priced through your candidate rate card, and the reply reports the per-lane and rolled-up delta between the two.
Because the replay uses the real resolver, the projection matches how you are actually billed — down to most-specific-country-wins matching and the round-up, never-under-bill convention on per-lane totals. Only lanes a per-unit rate card can meaningfully re-price are replayed (messaging sends and voice minutes); meters like recording or AI-agent tokens are skipped.

2. Where it lives

Open Billing → What-if pricing simulator in the dashboard. The page is role-gated to owner, admin, and billing, matching the gate on the sibling billing drill-downs. The page has two input groups:
  • Traffic window24h, 7d, 30d, or 90d. This maps to a window_days of 1, 7, 30, or 90 on the API.
  • Adjustments — one global slider (−50% to +50%) applied across every channel, plus an editable candidate rate per channel row. A typed rate overrides that channel and ignores the global slider; channels you do not touch follow the slider.
Results render as three headline cards — current spend, projected spend, and the signed delta with its percentage — above a per-channel replay table: volume, current spend, observed blended rate, the editable candidate rate, projected spend, and the change. A positive (red) delta means the candidate card costs more on your mix; a negative (green) delta means it costs less.

3. Reading a what-if preview

Every reply — dashboard row or API lane — is a current-vs-candidate comparison: All money fields are in 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). A candidate row prices a lane through the same ladder your live rates use:
  • rate_per_unit — wholesale base, USD per unit (per message, or per voice minute).
  • markup_bps — proportional markup in basis points (500 = 5%), layered on the base.
  • markup_fixed_cents — flat markup added per unit, in cents.
  • absolute_price_cents — a fixed per-unit price in cents. When set, it wins over the base-plus-markup math entirely.
Which row in a multi-row card applies follows most-specific-wins: an exact country match beats the * wildcard; within that, an exact sub-type match beats a sub-type-agnostic row. Direction must match too — an mt row never re-prices an mo lane.

Dashboard vs API granularity

The dashboard models one rate per channel: a typed rate is sent as a wildcard (*) mt row for that channel, and the result is rolled up to one table row per channel. Two consequences to know:
  • Outbound-only. A typed dashboard rate re-prices mt (outbound) lanes only; inbound lanes keep their current cost. To model mo pricing, use the API with direction: "mo".
  • Truly global. The wildcard row re-prices the channel across every destination and sub-type uniformly. To model per-country or per-sub-type pricing (e.g. WhatsApp marketing vs utility to Nigeria), use the API with explicit country_code / sub_type rows.

4. The API

POST /api/v1/billing/whatif-pricing/preview — requires the owner, admin, or billing role. The card is a partial override: lanes with no matching row keep their current cost and report candidate_matched: false with zero delta. You only model the lanes you intend to change.
Read the lane list for coverage before you read the totals: any lane with candidate_matched: false is traffic the candidate card does not price — those lanes stay at current cost in your totals, so a card that covers only part of your mix understates its own impact. matched_lane_count against lane_count in totals tells you the coverage at a glance.

5. Limits and the read-only guarantee

The simulator is a compute surface, safe to run as often as you like:
  • Nothing changes. A preview writes no record, emits no billing event, and moves no money. Your live rates, your pricing overrides, and your invoice are untouched no matter what you model.
  • Window caps. window_days is bounded at 1–90 days; the dashboard offers 1, 7, 30, and 90. Replies cover up to 200 distinct lanes (the largest by quantity first), and a single request accepts up to 5000 candidate rows.
  • A projection, not a forecast. The replay holds your recorded traffic fixed and changes only the rates. Final invoices still vary with delivery outcomes and any negotiated overrides on your account.
The simulator differs from the per-card pricing cells you can edit under Billing → Rate cards (/billing/rate-cards): those cells ask “what would one unit cost at this price under this card?” and are instant lookups against a card’s rows. The simulator asks “what would my last 90 days have cost under this card?” and re-prices your real usage lane by lane. For the rate-card library itself, see Rate cards.

6. Worked scenario — price WhatsApp to Nigeria +5%

Model a +5% markup on outbound WhatsApp to Nigeria over the last 30 days, the way a negotiated volume sheet would quote it:
  1. Send the candidate card as one row: channel: "whatsapp", country_code: "NG", direction: "mt", your wholesale rate_per_unit, and markup_bps: 500.
  1. Read the per-lane results. With no sub_type on the row, it re-prices every outbound WhatsApp-to-Nigeria lane — marketing, utility, and untyped traffic alike — so the WhatsApp/NG lanes show candidate_matched: true with the +5% delta. To price sub-types differently, send one row per sub-type (e.g. marketing at 500 bps, utility at 200 bps); the exact sub-type match wins per lane.
  2. Read totals. delta_cents and delta_percent are the whole-window impact of the change on your mix; matched_lane_count confirms the card actually covered the Nigeria lanes you intended, and every other channel’s lanes show zero delta.
In the dashboard the same experiment is quicker but coarser: set the window to 30d and type a candidate rate in the WhatsApp row. That models the * wildcard across all destinations, so use the API when the question is country- or sub-type-specific, as above.