> ## 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.

# Inbound cost metering: received traffic is reported, not debited

> Received SMS/MMS and inbound voice minutes are priced through the same rate resolution as sends, but the inbound-cost report only reads persisted volume. What the report contains, how its lines foot to the total, and how pricing_source attributes each rate.

# Inbound cost metering

Outbound sends and inbound traffic run through the same pricing engine but
live on different planes: a send resolves a price and debits your wallet in
the same breath, while received traffic is priced and reported without ever
touching the wallet. This page is the model for that second plane — what
`GET /api/v1/usage/inbound-cost` reports, why it can never move money, and
what to reconcile it against.

## 1. A reporting plane, not a metering plane

Spend tracking in Orbit grew up outbound-first. Every send resolves a
charge through the shared rate-resolution step and writes it through the
wallet ledger, so the usage and billing dashboards always knew what you
spent sending. Received traffic never got the same treatment: SMS and MMS
that arrive on your numbers, and voice minutes that terminate to them, are
billed by carriers per message and per minute — but nothing priced that
volume, so any "total spend" view you built silently excluded it.

The inbound-cost report closes that gap by reading what already happened
instead of metering as it happens:

* **Outbound meters.** The send path writes one usage record and debits
  the wallet synchronously — the loop described on
  [the usage metering pipeline](/concepts/usage-metering-pipeline).
* **Inbound reports.** The report aggregates the volume your tenant
  already persisted — received messages from the `messages` table and
  received calls from `call_logs` — and prices that volume on demand for
  the window you ask for. Nothing is written and nothing is charged; the
  figure you get is a read of recorded volume at current rates.

Because the report is computed at request time from persisted rows, it
covers received traffic regardless of when the rating features shipped:
last month's received messages price today, the moment you ask.

## 2. The response contract

One request covers one window and answers with one total plus one line per
channel that carried received traffic:

```json theme={null}
{
  "period": "2026-06",
  "range": { "from": "2026-06-01T00:00:00.000Z", "to": "2026-07-01T00:00:00.000Z" },
  "currency": "USD",
  "total_cost_cents": 1216,
  "lines": [
    {
      "channel": "sms",
      "unit": "per_message",
      "quantity": 900,
      "unit_price_cents": 0.8,
      "cost_cents": 720,
      "pricing_source": "default_markup"
    },
    {
      "channel": "voice",
      "unit": "per_minute",
      "quantity": 310,
      "unit_price_cents": 1.6,
      "cost_cents": 496,
      "pricing_source": "fallback"
    }
  ]
}
```

* `period` labels the window (a `YYYY-MM` calendar month by default).
* `range.from` / `range.to` are the exact half-open interval the lines
  aggregate, so two reports can always be compared by interval.
* `currency` is the ISO-4217 code of your wallet currency — the same
  currency every other billing surface reports in, so this figure can
  never disagree with your balance about which currency it speaks.
* `lines[]` carries one entry per channel with received volume: the
  `channel` (`sms`, `mms`, `voice`), the billing `unit` (next section),
  the `quantity` in that unit, the per-unit `unit_price_cents`, the line
  total `cost_cents`, and the `pricing_source` attribution (section 4).
  Lines are ordered most-expensive first.

The money invariant: **the lines always foot to the total.** Every
monetary figure at the contract is integer cents, `total_cost_cents` is
the exact sum of the per-line `cost_cents` values, and there is no sub-cent
drift between the breakdown and the total. Per-unit prices may carry
sub-cent precision (0.8 above); each line rounds to whole cents once, and
the total adds those rounded lines.

The window itself is chosen with a calendar month, a rolling window, or an
explicit from/to pair — the same options as the rest of usage reporting;
the API reference carries the full parameter contract.

## 3. The two billing units received traffic uses

Inbound traffic bills on exactly two units:

* **`per_message`** — received SMS and MMS. Each received message counts
  as one, exactly as the carrier bills termination. There is no segment
  arithmetic here: [SMS segments and encoding](/concepts/sms-segments-and-encoding)
  can multiply the cost of a long *outbound* message because the platform
  pays per segment, but a received message is a single billable event
  regardless of its length or encoding.
* **`per_minute`** — inbound voice. Each received call rounds up to the
  next whole minute (a 61-second call bills as two minutes), mirroring how
  carriers bill termination; `quantity` is the sum of those rounded
  minutes over the window.

Outbound, the same pricing ladder works in per-segment quantities for SMS
and per-minute quantities for voice — the ladder is shared, only the
measured quantity differs. One deliberate scope note: free or
bring-your-own inbound channels (WhatsApp, email, and similar) carry no
per-event carrier termination cost, so they do not appear in the report;
including them would over-state your spend.

## 4. The same resolution chokepoint, with direction "mo"

Pricing a received message uses the same rate-resolution step as pricing
a send — `resolvePrice()`, the chokepoint documented on
[pricing and rate resolution](/concepts/pricing-rate-resolution) — with the
direction set to `"mo"` (mobile-originated / inbound) and the country
wildcarded to `*`, which resolves the global inbound rate for the channel.
The report also shares the batched ladder read (`resolveBaseRatesBatch()`):
every channel's rate shape resolves in one pass instead of one lookup per
line, and org markup overrides still apply per channel exactly as they do
for sends.

Because both passages share the ladder, each line's `pricing_source` names
the rung that produced its unit price, using the same attribution labels
as outbound rating:

* `org_markup` — the base inbound rate marked up by your organization's
  own override.
* `default_markup` — the base inbound rate with the platform-default
  markup.
* `fallback` — no inbound rate row matched the channel yet, so the
  per-channel fallback price was used; the report therefore works before
  per-country inbound rate sheets exist.
* `absolute_override` — an absolute per-unit price set for your
  organization, verbatim.

The symmetry stops at rating. An inbound price never walks
[the outbound billing gate chain](/concepts/billing-gate-chain-model) and
never consults the pause flags that can hold a send: there is nothing to
gate, because an inbound price never becomes a charge.

## 5. Reporting-only invariants

Three guarantees define the plane:

1. **It never mutates the wallet.** No debit row, no credit deduction, no
   authorize/capture hold — a report request cannot change your balance.
2. **It never calls a provider.** The report is a pure read of persisted
   volume plus the rate ladder.
3. **It reconciles to deliverables, not to ledger rows.** You will not
   find a wallet transaction matching an inbound line, because none
   exists — this is the explicit contrast with
   [the wallet ledger audit trail](/concepts/wallets-credits-and-charges),
   where every outbound debit is a row you can point at. To reconcile an
   inbound line, re-derive it: filter your received messages and calls to
   the same `range`, count messages and carrier-rounded minutes, and
   multiply by the line's `unit_price_cents`.

Re-pulling a past window is a re-read of append-only rows at current
rates — a recompute, never a re-charge — the same replay property the
outbound rollups rely on.

## 6. Where this page sits in the billing family

* [The usage metering pipeline](/concepts/usage-metering-pipeline) — the
  outbound loop. Its aggregation stage already counts inbound rows (its
  counter rollups name "sms-inbound, sms-outbound" alongside voice), but
  stops at counts; this page is the cost side of those inbound counts.
* [Pricing and rate resolution](/concepts/pricing-rate-resolution) — the
  precedence ladder both directions resolve through, and the home of the
  attribution labels.
* [Wallets, credits, and charges](/concepts/wallets-credits-and-charges) —
  the append-only ledger the inbound plane deliberately never writes.

## 7. Where to see it live

* **Dashboard.** The Billing → Usage page shows received-traffic cost
  next to your outbound spend.
* **API.** `GET /api/v1/usage/inbound-cost` returns the report for your
  tenant.
* **Guide.** [Inbound traffic cost metering](/guides/inbound-traffic-cost-metering)
  walks worked endpoint calls and reads a breakdown end to end.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.