Per-operator SMS rates
A single per-country SMS rate is only an average of the operators inside that country, and carriers do not cost the same. When your traffic concentrates on one network, the country row misprices every message to it — in both directions. This guide covers the per-operator rate decks that fix that: how a send resolves its operator, how the resolver walks the rate chain, how you maintain the SMS deck yourself by CSV, and how to prove which tier priced any given send. The pricing model behind this page is Pricing and rate resolution. Read that page first if you are new topricing_source and the override precedence; this guide is the operator-level, day-to-day companion to it.
1. The two resolution chains: voice vs SMS
Every priced send resolves an operator for the destination first, then reads the rate that operator carries. Voice and SMS build that operator identity differently:- SMS — country + MCC/MNC → operator → rate. The Mobile Country Code (MCC, 3 digits) and Mobile Network Code (MNC, 2–3 digits) together identify one operator, e.g.
432+11=432011, Hamrah-e-Avval (MCI) in Iran. On the send path the MCC/MNC comes from the HLR lookup of the destination number. On the preflight that prices and debits before the HLR responds, the operator is resolved from the GSMA dial-code deck instead: the longest prefix of the destination number that the deck knows (a number starting98912resolves to MCI) supplies the MCC/MNC early. A dial-code miss never fails a send — the price simply falls back to the country row. - Voice — country + dial code → operator → rate. Voice destinations resolve by longest-prefix match of the E.164 number against the per-prefix rate deck: the most specific stored prefix wins (
+98912beats+98,+98beats the country row). The per-prefix deck is the voice analogue of the MCC/MNC deck.
2. How the resolver walks the chain
Every send, voice or SMS, resolves its price through one shared per-send resolver, so the card you read and the wallet debit always agree. For an SMS send carrying a resolved MCC/MNC, the resolver consults these tiers in order and stops at the first match:- Your per-network override — a fixed rate you published for one operator from Billing → MCCMNC Overrides. Applied verbatim: no markup on top. This is the strongest tier and beats every markup-based rule.
- The published per-operator rate — the operator’s wholesale cost in the operator deck, times markup (your organization’s markup when one is set, otherwise the platform default). This is the tier that makes “price by operator, not by country” real for every operator the deck covers.
- Your organization’s country row — a rate row scoped to your organization for that country and channel.
- The platform country row — the default per-country rate for the channel.
- The wildcard row — the channel’s catch-all when no country row exists.
- The per-channel hard default — the last-resort floor used when no row at all exists (for example $0.02 per SMS segment). A send priced here is a signal that a rate row is missing, not a negotiated price.
GET /api/v1/pricing/mccmnc-rates returns each operator row with a pricing_source of absolute_override (tier 1), org_markup / default_markup (tiers 2–4), or null when no published rate exists at all. A null row reads Contact sales on the card — it is not a zero price, and nothing is billable from it. Voice sends run the identical walk with the prefix deck in place of the MCC/MNC deck.
3. Maintain the per-operator deck: bulk CSV import
The tenant-owned surface for operator-level rates is Billing → MCCMNC Overrides at/billing/mccmnc-overrides, role-gated to owner and admin. Single rows are created and edited through the two-step form (edit fields, then review with a mandatory reason); bulk work goes through Import CSV in the page header.
The import dialog parses the file in your browser, shows a confirmation step with the affected scope (row count, distinct organizations, and a per-row preview), and only applies rows after you click Apply. The file needs a header row; columns are case-insensitive:
organization_id(ororg_id) — the organization the rate applies to. Owners and admins may only target their own organization.mcc— exactly 3 digits;mnc— 2 or 3 digits. The pair is normalized to the canonical zero-padded network code (432+11→432011), the same key the resolver looks up, so11and011land on the same row.rate_per_unit(orrate) — the customer-facing price per unit as a decimal, incurrency(ISO-4217, defaults to USD). The rate must clear the network’s current wholesale cost; a rate below cost is rejected so a deck typo can never sell traffic below what the carrier charges.effective_from/effective_to— optional effective window; a future-dated row is not applied until it starts, an expired row stops applying.notes— free text carried on the row for your own bookkeeping.
4. Maintain the per-prefix deck (voice)
The voice side of the chain — the per-prefix deck that longest-prefix-match reads — is maintained in the platform’s voice-destinations console, not in your dashboard: per-prefix voice rates are a platform-owned deck, and there is no tenant upload for it. What you own on the voice side is exactly what you own on SMS: your organization’s overrides and markups, and the diagnosis below when a destination prices wrong. The walk itself (most-specific prefix first, then country, then wildcard, then the per-channel floor) is the same ladder described in section 2 and modeled in Pricing and rate resolution; the rate card for what your voice destinations currently resolve to is on Rate cards.5. Diagnosing a mis-priced send
When a bill line looks wrong, work backwards from the record to the missing row:- Read the CDR / message record. The ledger row and the message record carry the attribution: which rate row or override produced the charge, the wholesale cost, the markup applied, and whether a per-operator rate actually matched for the destination. A send whose record says no operator rate matched was priced on the country ladder or a coarser tier.
- Read the rate card for that network.
GET /api/v1/pricing/mccmnc-rates(the same rows Billing shows) labels each operator with itspricing_source. Compare the label on the row for your destination’s MCC/MNC with the tier you expected. - Locate the gap.
pricing_source: nullmeans the deck has no row for that operator — the send priced on the country row or wildcard. A country-labeled charge where you expected an operator rate means your override or the operator row is missing or outside its effective window. A hard-default charge (the per-channel floor) means even the country row is missing. - Close the gap on the tier you own. Publish the missing operator rate from Billing → MCCMNC Overrides (form or CSV). The write invalidates the pricing cache immediately, so the next priced send resolves on the new row — there is no cache TTL to wait out. For a gap on a platform-owned tier (a missing country or prefix row), the diagnosis above is exactly the evidence to bring to support.
6. Rate resets after an override, and the audit trail
An override holds until you change or delete it. Deleting an override reverts that network to the resolved price from that point on — operator cost times markup, or the country ladder when no operator row exists — and the confirmation dialog states that revert before you confirm. Every create, edit, import, and delete is written to the pricing change log with the actor, the timestamp, the before and after rate, and the reason you entered at review; the tenant-facing change history (GET /api/v1/pricing/changelog) exposes that trail, so any rate your finance team questions can be traced to the change that set it. Because each write invalidates the resolver cache, “reset” means the next send, not the next cache expiry.
7. Worked example: Iran MCI 0.2099 vs the 0.2757 country row
Iran’s country row must cover its most expensive network, RighTel, at a cost of 0.2757 per message. The two large networks, MCI (Hamrah-e-Avval,432011) and Irancell, cost 0.2099. With only the country row published, every MCI message is priced off 0.2757 — a 31% overcharge on the network that carries most of the traffic, with the margin silently subsidizing the marginal one.
With the operator deck in place, the send path resolves +98 912 … to MCI before pricing (via the dial-code deck on preflight, via HLR on the send path) and prices the operator row instead:
The same arithmetic runs in reverse for a tenant whose contract quotes a fixed per-message price on one network: publish that number as a verbatim override from Billing → MCCMNC Overrides and the resolver applies it exactly, markup excluded (tier 1 in section 2). After either change, verify on the rate card that the row for
432011 now shows the expected pricing_source and your_rate — the card labels the rule the wallet bills under, so a correct card row is a correct charge.
Related pages
- Pricing and rate resolution — the precedence this guide operates, and how the resolved price reaches the wallet ledger.
- Rate cards — the country-level card the operator deck refines.
- MCCMNC overrides — the verbatim per-network override surface and its API.
- CDR usage export and billing reconciliation — reconciling rated amounts against the ledger.
- Billing API reference — endpoint schemas for overrides and the pricing change history.