Skip to main content

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
The campaign price in three lines of Node — the round-up mirrors the wallet’s whole-cent debit from the rounding callout below:
Node.js
12,480 single-segment US messages at $0.0181 project to $225.89. Bodies longer than one segment multiply the rate by the segment count step 3 reports.

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.
The same numbers drive the dashboard: the what-if pricing simulator replays a window of your traffic against a candidate card, and cost intelligence tracks actual spend against projections like the ones above.

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
A lane no candidate row covers comes back with "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
Each row carries exactly the fields shown above — identity, lane, per-unit price and currency, provider, scope, and effective window. Internal costing and provenance fields (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).
Tenant view (sanitized projection):
200
Super-admins get the raw rows back instead, with the operator 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
The same super-admin scope filters the picker and override surfaces use — /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
SMS is metered per segment (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
Bring-your-own-credential channels (WhatsApp, Meta’s other surfaces, LINE, Telegram, push) price at $0 — the platform bills only what it terminates, and the per-channel fallback default keeps the row list complete either way.

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
The outbound side is the customer-facing per-minute price — routing is a separate concern that stays internal.

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
Every tier bills one blended per-participant-minute rate; add-ons you see outside 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
Read 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
An override priced by 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.