Skip to content

Billing ​

API ​

Full parameters and responses: balance, credits, usage, invoices, payment methods, plans.

GET  /api/v1/billing/balance
GET  /api/v1/billing/ledger
GET  /api/v1/billing/ledger/reconcile
GET  /api/v1/billing/usage
GET  /api/v1/billing/usage/summary
POST /api/v1/billing/credits/purchase
GET/PUT /api/v1/billing/auto-reload
GET/POST /api/v1/billing/payment-methods
POST /api/v1/billing/payment-methods/setup-intent
POST /api/v1/billing/payment-methods/{id}/default
DELETE /api/v1/billing/payment-methods/{id}
GET  /api/v1/billing/invoices
GET  /api/v1/billing/invoices/{id}
GET  /api/v1/billing/invoices/{id}/download
GET  /api/v1/billing/plans

Two models ​

Pay as you goEnterprise
FundingPrepaid credit walletContract with committed spend
EnforcementCalls stop when credit runs outInvoiced monthly
PricingPublic catalogueNegotiated per-customer prices

The credit ledger ​

credit_ledger is insert-only. A balance is the sum of its rows, never a mutable column, so a balance can always be explained by the entries that produced it and ledger/reconcile can prove the two agree. Amounts are decimal(20, 8) with the ISO currency stored alongside.

Metering ​

Usage is metered per component, not per call: transcription seconds, model input and output tokens, synthesis characters, telephony minutes, storage, and the platform fee. Each becomes a usage_events row carrying the provider's own units and the price applied, so an invoice line can be traced to the second of the call that caused it.

A component is priced only from a registry row with a verified price. Where no verified price exists the row is still written — the quantity is not lost — but with cost = 0 and priced: false, and the breakdown reports partial coverage rather than presenting an under-count as a total. See Analytics & cost.

Provider cost vs price ​

Provider cost is what CallAgent paid; price is what you pay. With BYO keys the provider bills you directly and CallAgent charges only its platform fee — both figures are still recorded, because "why is my bill this number" needs both.

Auto reload ​

When the balance falls under a threshold, a configured amount is charged to the default payment method. Guarded against repeated failed charges so a declining card does not generate a hundred attempts.

Concurrency and admission ​

Before any call, admission checks concurrency headroom, credit balance, organization status and destination policy in one call, and returns a slot token released on finalize — without it, a crashed call leaks a concurrency slot forever.

Stripe has never been called with a real key

Purchases, setup intents and payment methods all need Stripe keys, and none has ever been supplied. STRIPE_SECRET is empty in the shipped example environment and the gateway throws a named error — "Payments are disabled until the owner supplies Stripe keys" — rather than degrading quietly.

Every Stripe path is covered by tests that drive the real Stripe SDK, so request signing and serialisation are exercised. Every HTTP response in those tests is a canned fixture. No live or test-mode Stripe endpoint has ever been contacted.

Webhook signature verification, idempotent event handling and ledger-authoritative crediting are all real code that has never met Stripe. See What is not built.

Documented against the code. Anything not yet implemented is labelled, never described as working.