Estimate a send end-to-end
The generated operations below this overlay each document one call; a send decision chains three of them. This section is that round trip in order: look up the lane’s per-message rate, cost a planned campaign from a stored segment’s size, cost one composed body with its segmentation, then read the numbers against the rounding rules.Step 1 — Look up a destination country and channel
GET /api/v1/pricing/messaging returns the per-unit rate for every
message channel in one ISO alpha-2 country, with your organization’s
effective price already folded in. effectiveRatePerUnit is the
per-message number the wallet debits on that lane; unit says how it
meters (per_message for SMS and MMS). The country query is a
price-lookup key only — it never routes traffic. The full per-channel
walk, including the $0 bring-your-own-credential rows, is §8 below.
200
Step 2 — Cost a planned campaign
A stored segment already knows its audience:GET /api/v1/segments/{id} returns member_count next to the segment
object. Multiply it by the step-1 rate and apply the same round-up the
wallet uses.
200
Node.js
Step 3 — What-if a single message
POST /api/v1/messages/estimate is the body-aware companion to
GET /api/v1/pricing/estimate: send the exact to and body you plan
to send and it segments the body the same way the send path will, then
prices through the same resolver the wallet charges with — the quote
cannot drift from the charge.
200
segments × rate_per_segment = estimated_cost: two GSM-7 segments at
$0.0181. The sample body runs past the 160-character single-segment
limit — the endpoint counts segments the same way the send path splits
them. For mms and rcs the response carries no encoding or
segments: one flat per-message unit, and estimated_cost equals
rate_per_segment.
Step 4 — Currency and rounding rules
Currency. Every price is expressed in its
currency — USD unless
your lane is priced otherwise; per-org overrides inherit the lane’s
currency. Rates resolve per (channel, country, direction) lane, so the
number you preview is the number you are billed at.Rounding. Per-unit rates carry sub-cent precision. The wallet
debits whole cents, rounded up per message — a 0.825-cent lane bills
one cent per message. Estimates quote the unrounded per-unit rate on
purpose so you see the true rate; estimated_cost is the rounded-up
projection.Segments. SMS bills per segment, not per message: GSM-7 bodies
split every 160 characters (153 once concatenated) and UCS-2 unicode
bodies every 70 (67 concatenated). A two-segment send costs twice the
per-segment rate; POST /api/v1/messages/estimate returns the count
before you commit, so a unicode body never surprises you at send time.Worked pricing samples
The generated blocks below document every parameter and response shape; this overlay walks the three chains a tenant actually runs before a send decision: estimate one destination → read the effective rate sheet → replay traffic against a candidate rate card (the what-if call every sender runs before committing volume), read the current rate card → audit a rate change → explain the delta on a bill, and the operator-level SMS rate card with scope filters team admins use to compare organizations. Every response is the real{ data, meta } envelope — data carries the payload, meta
carries the request_id and timestamp. Quote meta.request_id when you
report a bad number.
The Node SDK does not wrap the pricing namespace yet (/pricing/rates in the
SDK targets the legacy per-country table, not these tenant routes), so the
typed tabs below use the SDK’s orbit.request escape hatch — you get the
SDK’s auth, retries, and envelope unwrapping against the raw REST path.
Python fronts client.request the same way.
1. Estimate one destination before you send
GET /api/v1/pricing/estimate resolves a single send to the per-unit rate
the wallet would actually be charged. Pass channel and a destination to
in E.164 form — the endpoint resolves the destination country itself. Add
allIn=true to stack the hidden-fee line items (US A2P 10DLC registration,
call recording, transcription, premium support) a “what does this lane
really cost” question needs; include narrows that list to named surcharge
codes.
200
2. Verify against your effective rate sheet
GET /api/v1/pricing/effective-rates is the tenant-facing read the what-if
and estimate checks reconcile against: the global platform card collapsed
with any per-org overrides down to the single row the billing resolver would
charge per (channel, sub-type, country, direction) lane. Each row shows the
bare base rate and the effective per-unit price in cents after overrides
(and, for SMS, the platform default markup) are layered on — so the number
you display cannot drift from the number the wallet debits. hasCustomRate
is set only when your own org carried the override, never for the platform
default.
200
3. Replay traffic against a candidate rate card (the what-if)
POST /api/v1/pricing/whatif-simulate is the call you run before committing
volume: it replays a window of your own observed traffic (one entry per
billable lane, in usage) against a candidate rate card
(candidateRates) and projects the cost side-by-side with what the traffic
was actually billed. Traffic is held fixed; only the rate card is swapped —
that is the what-if. It is read-only: nothing is written, nothing is sent,
and an INSUFFICIENT_BALANCE at send time later means the top-up reminder
was never a reason to skip this preview. Both arrays fill a real body — a
bare {} returns 422.
200
"unmatched": true and
"matchedRatePerUnit": null — read unmatchedLaneCount before you trust
totalProjectedUsd; a candidate card with coverage gaps under-projects.
4. Read the current rate card (admin)
GET /api/v1/pricing/rates is the super-admin read of the full platform
rate card — every lane, global defaults and per-org rows alike, ordered by
channel then country. Use it before you edit or retire a lane; the list is
capped at 5,000 rows.
200
costAmount, costCurrency, markupBps, source,
sourceRef, createdBy) are not part of this list response; bill off the
per-lane rate and effective window, and use the admin rate detail read when
you need the full row.
5. Audit what a rate change did to a tenant’s bill
GET /api/v1/pricing/changelog answers “when did my pricing change, and
what changed”. A super-admin sees the raw internal audit rows (actor,
reason, before/after snapshots) and may filter by organizationId; a tenant
API key gets the sanitized projection — which surface changed
(rate_card or org_override), the action, whether it was a global
rate-card change or org-scoped, and the timestamp — with the internal
fields stripped, and scope pinned to the caller’s own org plus global
changes. A tenant-supplied organizationId is ignored (no cross-tenant
read). Cap with limit (default 100, max 500).
200
changedBy, a
free-text reason, and the full beforeState / afterState snapshots —
shape preserved verbatim, internal fields included. Pair the tenant view
with /pricing/effective-rates to explain a line on a bill; pair the
super-admin view with /pricing/overrides?organizationId=... to scope the
audit to one tenant.
6. Operator-level SMS rate card with scope filters
GET /api/v1/pricing/mccmnc-rates is the customer-facing SMS rate card at
mobile-operator (MCCMNC) granularity, with your organization’s markup
already applied. Filter with search (operator name, country, or MCCMNC
code) and country, and page with cursor / limit. A row with no
published cost returns "your_rate": null — the “Contact sales” row — and
pricing_source attributes each row to the same rule the send-path resolver
bills under (override / org_markup / default_markup), so the preview
cannot quote a price the wallet would not charge.
200
/pricing/organizations (org rows with a hasCustomRates flag, all=true
for one round trip) and /pricing/overrides?organizationId=org_... — let a
team admin compare a tenant’s negotiated rates against the platform card
before answering a dispute.
7. Public price list (no key needed)
GET /api/v1/public/pricing publishes the platform pay-as-you-go price list
with the pricing-model manifest — one tier, no contact-sales gate, no forced
migrations. It is unauthenticated and IP rate-limited, so a prospect can
verify pricing before signing up. Pass ?country=US for the published
country-specific list; omit it for the baseline.
200
per_segment), not per message: a GSM-7
body splits every 160 characters and a UCS-2 (unicode) body every 70, so a
long send costs N× the per-segment rate. Voice bills per minute, fax per
page. Bring-your-own-credential channels (WhatsApp, Instagram, Messenger,
push, Apple Messages, LINE) are listed with "billed": false at $0 — the
platform only charges for what it terminates itself.
Rate-card data is scoped to your own organization: tenant keys can read
only their own effective rates and overrides, never another tenant’s
negotiated pricing. Rates are resolved in the currency of the lane they
price (USD by default). This is a tenant-owned control — the platform
enforces the scope, you choose how far the card travels.
8. Preview per-country messaging rates by channel
GET /api/v1/pricing/messaging answers “what does it cost to message this
country, channel by channel” in one call: SMS, MMS, WhatsApp, RCS, and
Viber per-unit rates for the ISO alpha-2 country you pass, with your
organization’s effective price already collapsed in
(effectiveRatePerUnit). A channel with no country-specific row falls back
to the published platform default. hasCustomRate flips true only when a
per-org override applied. The country query is a price-lookup key — it
never routes traffic.
200
9. Preview voice rates for a country
GET /api/v1/pricing/voice is the per-minute counterpart: inbound (mo)
and outbound (mt) voice rates for one country, split into two arrays the
same way Twilio’s Pricing API splits them. Per-minute billing, USD, with
the same override-collapse the message preview applies.
200
10. Preview phone-number rental prices
GET /api/v1/pricing/phone-numbers prices DID inventory before you buy:
the monthly rental of each purchasable number type in one country, with the
cheapest available cost and how many are in stock. Only unassigned,
purchasable inventory is counted, so tenants can never enumerate which DIDs
already belong to someone else. A country with no purchasable inventory
returns an empty phoneNumbers list, not an error.
200
11. Read the RTC-PaaS rate card
GET /api/v1/pricing/rtc-rate-card publishes the whole video/audio room
pricing sheet in one response so a simulator or pricing UI can render
without a round trip: the per-participant-minute price for each resolution
tier, the add-ons bundled into that rate, and the three add-on fees billed
on top (extra RTMP destinations, recording storage, dedicated SFU). Pair it
with POST /pricing/rtc-simulate (§12) to forecast a workload from the
same numbers.
200
bundledAddOns are the optional extras below.
12. Forecast an RTC workload
POST /api/v1/pricing/rtc-simulate turns a workload spec into a line-item
forecast that mirrors the eventual invoice: resolution tier, average
participants, minutesPerRoom, roomsPerMonth, and the optional extras.
The same math runs in the dashboard simulator, so the forecast cannot
drift. Volumes here are illustrative — the endpoint never aggregates your
total R&D spend.
200
blendedRatePerParticipantMinuteUsd when you want the single
defensible blended rate for a budget line — it folds the extras back into a
per-minute number.
13. Pick an organization for the rate-card editor (admin)
GET /api/v1/pricing/organizations fills the super-admin rate-card
editor’s org picker: one row per organization with a hasCustomRates flag
that flips when the org has at least one per-org override, so the operator
can jump straight to tenants with custom pricing. Keyset-paginated by
(name, id); pass all=true to load the whole roster in one round trip
instead of cursor-walking.
200
14. Review per-org override lanes (admin)
GET /api/v1/pricing/overrides lists the negotiated per-org override rows
— optionally scoped with organizationId, otherwise the active overrides
across every tenant. Use it to answer “which lanes does this tenant price
custom on” before a support dispute. Super-admin only; capped at 5,000
rows.
200
absolutePriceCents (like this one) replaces the
base rate outright; markupBps / markupPercentage / markupFixedCents
layer on top of the base instead — one mechanism per row.
15. Errors worth branching on
The envelope mechanics —error.code / message / status / details,
retry order, retriable-vs-terminal — live once in the
error-handling guide.
Here is what the branch decision on THIS page comes down to.
Treat
422 and 403 as terminal, not retriable — a corrected request fixes
the first; the second needs a super-admin key or a switch to the
tenant-facing read. 429 and 401 are retry-once-after-fix and
re-key-then-retry respectively. The pricing reads draw from the same
RATE_LIMIT_AUTH_WRITE bucket as the other authenticated reads, and the
public list has its own IP bucket — the envelope’s
error.details.retry_after says how long.
These samples compound; the auto-generated per-operation entries below
always carry the full parameter table even where a sample here already
covers the op.
Further reading: the billing cost calculator
guide sizes a first top-up from these rates, the
what-if pricing simulator guide walks
the dashboard surface, and the
billing what-if preview + spend/burn recipe
(API recipes §22) chains the preview, burn-rate, and spend-series calls.