Skip to main content

Inbound traffic cost metering

GET /api/v1/usage/inbound-cost reports what your received traffic — inbound SMS, MMS, and voice — costs over a period or rolling window. It closes a blind spot in spend tracking: every outbound (MT) send is rated and shows up on the usage and billing surfaces, while the received (MO) volume carriers bill on your numbers was never metered. If you run contact inquiry lines, callback numbers, or two-way support, a share of your real carrier spend only ever appeared in one direction. This endpoint prices that share so “total spend” actually means total.

1. Why inbound differs

Inbound traffic arrives unsolicited: a customer texts your support DID, a prospect calls the number in the ad, a subscriber replies STOP to a campaign. Two consequences follow:
  • Volume is customer-driven, not campaign-driven. You can’t forecast inbound the way you forecast a send — it follows contact inquiries, inbound callbacks, helpdesk conversations, and message replies your own traffic generates.
  • The billing surface has historically been outbound-centric. The usage, billing, and spend-anomaly surfaces all report MT spend through the pricing and wallet surface. Received SMS/MMS on your leased numbers and inbound voice minutes are billable events too, but nothing priced them — so “total spend” silently excluded received volume.
/usage/inbound-cost aggregates your persisted inbound volume and prices it with direction: "mo" (mobile-originated) through the same rating every other usage surface uses. It is reporting-only: it never touches a wallet, deducts credits, or calls a provider — it reads recorded traffic and resolves rates so received spend is visible and budgetable.

2. Read the totals

Window forms

The endpoint accepts three ways of choosing the window; the "mo" rating and response shape are identical across all of them. Precedence when several are passed: rolling window wins, then from+to, then calendar period.

Working with the response

Two interpretation rules apply to every response:
  • The period label echoes the month you asked for (or the month containing from / the window’s end); the range is the authoritative [from, to) interval the totals cover. A rolling-window response also carries window back.
  • All money is integer cents in currency — the endpoint reports in the same wallet currency every other billing surface uses, and the per-line cost_cents always sums exactly to total_cost_cents.

3. What makes up each channel line

Each line is one channel’s rated received traffic for the window. What’s included per channel — and what isn’t. Each line also reports unit (headline billing basis: per_message or per_minute), unit_price_cents (the resolved rate), and pricing_source — attribution for where the rate came from, the same ladder the wallet charges through (org_markup, default_markup, mccmnc_floor, fallback, …). A fallback source means no configured inbound rate row existed and the per-channel fallback rate was used — never a fabricated or silent rate.

4. What decides the price

Two inputs move a number: the inbound rating and the origin network.
  • MO rating. Each bucket is rated as direction: "mo" against the global country code * — the global inbound rate configured for the channel, falling back to the per-channel fallback when no inbound rate row has been uploaded. Org-level overrides, below-cost floors, and currency conversion apply exactly as they do to outbound pricing, so a configured MO rate and an MT rate never diverge from different pricing rules.
  • Origin network. The counts read operator-billable voice duration (per-call minutes rounded up, matching how carriers bill inbound duration) and per-message MO volume. The endpoint prices the volume that was received; it does not project what a missed or abandoned call would have cost.
Match this against the tally use cases — customer contact lines, support callback numbers, subscriber replies to campaigns — where the received leg of the conversation is exactly what this endpoint is built to price.

5. Sample queries and dashboards

Three patterns cover most inbound-cost checks:
  1. Monthly budget line. Pull ?period=YYYY-MM once per closing month and post total_cost_cents against the outbound usage total. Received spend that used to be invisible now foots against your full carrier budget, month over month.
  2. Rolling-window watch. Pull ?window=24h on a cron and alert when total_cost_cents jumps day-over-day beyond your envelope — an inbound abuse or routing loop surfaces as an inbound-cost spike before the carrier statement would.
  3. Channel composition. Slice the response on lines[].channel and graph the composition per channel over time. A support-mainly-inbound month will show a voice-heavy split; a reply-heavy campaign month shows it in sms.
The same endpoint backs dashboards and alerts — because the lines carry a stable schema (channel, unit, quantity, unit_price_cents, cost_cents, pricing_source), date-, channel-, and rating-source breakdowns can be built on the response alone.

6. Refund and measurement caveats

Read these before you treat the report as an invoice:
  • Not a refund surface. The report tells you what received traffic cost as rated — it doesn’t fetch provider credits, dispute a rate, or reconcile against a carrier invoice. If a carrier over-bills an inbound leg, that’s a carrier dispute; this report shows what your rated view says it should have cost.
  • Partial coverage returns what succeeded, not an error. Each channel aggregate is independent: if a scan is unavailable (e.g., a schema predating a table), that bucket is skipped — the response returns the channels that resolved and a zero-omitted set. A window with no received traffic returns lines: [] and total_cost_cents: 0, not a 5xx.
  • Inbound-only scope by construction. Historical schemas without a messages / call_logs table, and received MMS/voice on channels outside those persisted surfaces, simply contribute nothing — the report never estimates unrecorded traffic into the total.
  • Statement timeout as a guard, not a retry. Each aggregate runs with a bounded statement timeout; a pathological full-window scan returns an explicit error rather than riding into a silent edge failure. Treat a bounded-timeout error as “the window is too hot to read right now,” narrow the window, and retry — it is a read constraint, not lost data.
  • Cost intelligence — outbound cost surfaces, anomaly views, and burn-rate reporting the inbound complement.
  • Billing cost calculator — presale cost-walk lanes for MT traffic; inbound metering fills the received side.
  • Usage anomaly alert rules — the tenant-configured rules that flag spend envelopes; inbound-cost totals are the inbound-side comparator.
  • Rate cards — the rate-card library pricing_source ladder rows map to.