The telecom pattern is one of the most operationally complex in commercial billing - carriers run prepaid wallet balances, postpaid recurring plans, included allowances per metered dimension, per-destination-country roaming pricing, family-plan pooled allowances, and overage handling, all inside a single subscription. Every primitive in Kontier shows up somewhere in a carrier's billing model.
Real-world examples. T-Mobile, Vodafone, Verizon, AT&T, Free Mobile, Vodafone Italy, Telekom Deutschland, Orange, O2, Three UK, Mint Mobile. Common shape: monthly plan with X minutes + Y SMS + Z GB included, additional usage at per-unit rates, roaming priced per destination country and per service type, family plan with pooled allowance across N lines, prepaid option with topup-and-spend wallet model.
The shape of the problem
Telecoms hit nearly every operational complexity dimension at once:
Prepaid + postpaid coexistence. The same carrier offers customer-funded wallet balances (prepaid SIMs - load €20, talk until empty) and monthly recurring plans (postpaid - bill at the end of the month). Often one customer has both.
Multi-dimensional metered allowances. A single plan includes minutes, SMS, and data, each with their own allowance and own overage rate. Real-time visibility per dimension is non-negotiable.
Roaming as keyed pricing. International roaming charges per destination country and per service (call vs SMS vs data). Modeling each as a separate product creates 200+ entries per service; modeling roaming as a keyed product (one product per service, attached to a key set, with one price per destination-country price_key) collapses to one product.
Family plans / pooled allowances. A family plan has 5 lines but shares a pooled allowance for data; minutes might be per-line. Pool semantics live at the subscription level, per-line semantics at the line level.
Real-time hard caps. Customers expect to be able to set "stop me from using more than 5 GB this month" caps that actually enforce in real time, not "send me an invoice after the fact."
Mid-cycle plan changes. Upgrading from a 5GB plan to a 20GB plan mid-cycle triggers immediate access to the new allowance (less the consumed portion of the previous one) and prorated billing.
Kontier blueprint
Concern
Kontier primitive
Prepaid SIM balance
Wallet per customer; reserve before the call with POST /v1/reservations, capture with the usage event
Monthly recurring plan
A plan with a flat-fee product and metered products, sold as one subscription
Per-dimension allowance
included_quantity on each metered product's plan membership
Per-destination roaming
Keyed product: a key_set_id on the product, a price_key on each price, subscription line and usage event
Family plan pooled allowance
One subscription for the household; every SIM's events carry its subscription_id
Mid-cycle plan upgrade
Cut the live revision now and continue on the new plan's lines; the cut is prorated
Build it
IDs in the examples are placeholders: use the id each create call returns.
Each metered product needs a meter bound to it (POST /v1/meters, then POST /v1/products/{id}/meter-bindings with role: BILLABLE_QUANTITY); step 7 shows the data meter.
Roaming is one keyed product per service. The set of destination keys lives in a key set; each product opts in via key_set_id, and individual prices carry a price_key matching one of the set's entries. Create the key set once:
"Rest of world" is handled by a single set price (is_set_price: true, no price_key): the resolver falls back to it for any key in the set that has no exact-match price, so you only price the destinations that differ from the catch-all rate.
2. Build the postpaid plan with allowances
Code
curl -X POST https://api.kontier.eu/v1/plans \ -H "Authorization: Bearer sk_test_…" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "Magenta Pro", "price_cadence_unit": "month" }'# Plan flat fee - a DECLARED-quantity product (no meter; quantity stated on the subscription line)curl -X POST https://api.kontier.eu/v1/products \ -H "Authorization: Bearer sk_test_…" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "Magenta Pro Plan Fee", "unit_label": "plan", "quantity_source": "DECLARED", "pricing_model": "VOLUME" }'
Attach every product to the plan. The metered domestic products carry their included allowance (1,000 minutes, 1,000 SMS, 20 GB = 20,000 MB); the roaming products carry none, so they bill from the first unit:
A keyed product's line must name a price_key its key set offers (otherwise the draft is refused with SUBSCRIPTION_LINE_PRICE_KEY), and keyed usage bills one line per key. So the deal carries one roaming line per destination it should bill. Roaming usage under a key the deal has no line for is accepted but billed by nothing; GET /v1/subscriptions/usage-without-line lists it, and a renegotiation that adds the line bills usage from its cut on.
6. Network ingests usage events
For every call, SMS, or data session, the carrier's network sends an event:
The first event consumes 5 minutes of the 1,000-minute domestic allowance; the second is roaming-priced from the first minute because roaming products carry no allowance. A CDR feed is better sent in batches: { "events": [ … ] }, 1 to 1,000 per call. GET /v1/subscriptions/{id}/allowance shows the remaining allowance per product in real time.
7. Hard cap on data with network enforcement
Hard caps are built on meter thresholds. A meter carries a list of thresholds; each declares a name, a comparator (gt, gte, lt, lte, eq) and a value over the meter's evaluated total. When usage crosses one, Kontier records a breach and emits the meter.threshold_breached webhook, which names the subscription, the threshold and the observed value.
Define the data meter with a threshold at 25 GB (25,000 MB) in the period:
Subscribe a webhook endpoint on your network OSS to meter.threshold_breached; when it arrives, the OSS blocks further data sessions for that subscription's MSISDN until the next cycle. No "I got a €500 surprise bill" support tickets.
Scope note. Kontier emits the breach signal and records the breach (GET /v1/meters/{id}/breaches); actually cutting off the MSISDN happens in the carrier's network. The threshold is defined on the meter itself, so it applies to every subscription that consumes the meter - per-subscriber caps with subscriber-specific limits are not first-class.
8. Upgrade mid-cycle
Lisa moves to a bigger plan. Cut the live revision now (POST /v1/subscriptions/{id}/revisions/cut), write the whole draft with a new last phase whose lines name the new plan (PUT /v1/subscriptions/{id}/revisions/draft), and publish (POST /v1/subscriptions/{id}/revisions/draft/publish). The cut is a billing boundary, invoiced at once: the invoice written at the cut credits the previous plan's fee for the rest of the period and bills the new one from the cut (see When a change is invoiced). The SaaS per-seat blueprint shows the three calls in full.
Variations
Prepaid SIM mode. Same products, sold product-direct (origin.kind: "product") with no plan fee, and the customer funds a wallet. Before serving a call, reserve funds with POST /v1/reservations; ingest the usage event with the hold's hold_id to capture it. wallet.low_balance and auto-topup (auto_topup_enabled) keep the SIM funded.
Family plan with pooled data. One subscription for the household; every SIM's events carry the same subscription_id and draw from one allowance. Put a SIM identifier in each event's metadata for per-SIM reporting, and use a meter dimensions entry to break usage down by it.
Unlimited plans with fair-use throttling. Set the data product's included_quantity very high and add a meter threshold at the fair-use level; your OSS throttles (rather than bills) on meter.threshold_breached.
Add-on data packs. The customer buys a one-time 5 GB pack: grant it with POST /v1/subscriptions/{id}/usage-credits (quantity: "5000" on the data product). Granted usage is spent before the plan's own allowance.
What you don't have to build
Per-dimension allowance bookkeeping with real-time drawdown
Roaming destination dispatch (keyed product + per-price_key prices, with an is_set_price catch-all)
Pooled-allowance family plan accounting
Hard-cap signalling to the network OSS (meter.threshold_breached)
Mid-cycle plan upgrade with prorated billing
Reconciliation between CDR ingest and the customer's invoice