Billing is a prepaid credit ledger. One credit is one US cent. You top up through Stripe-hosted checkout, and finished work debits the balance. This page covers the loop, how to read your own numbers, and the things that surprise people. The console surface is Developer → Billing.

The loop

  1. Add credits. POST /v1/organizations/billing/checkout takes amount_cents and returns a hosted checkout URL. The amount is priced inline, so there is no product catalog to pick from — any amount between 500 and 1,000,000 cents (5to5 to 10,000) works.
  2. Stripe reports the payment. A completed checkout credits the ledger. An asynchronous payment method credits when it settles instead.
  3. A refund claws the credits back.
  4. Usage debits the ledger as work finishes.
Checkout and the customer portal require a signed-in session. An API key gets 403 This action requires a signed-in session — spending money is a person’s decision, not a script’s. Everything else on this page works with a key.
So there is no curl for this one. The console’s Billing page sends the request on your behalf, carrying your browser session:
Send plan instead of amount_cents and you get a 400: the field survives only so an older client gets an explicit refusal rather than silently buying credits when it meant to start a subscription. There are no subscriptions. POST /v1/organizations/billing/portal opens Stripe’s own portal for payment methods and receipts. It answers 409 until you have made a purchase — the Stripe customer is minted at first checkout, so before that there is nothing to manage.

Reading your own numbers

Five endpoints sit behind the console’s Billing page, and all five take an API key.

Reconciling a bill

usage/breakdown carries three money columns, and reading the wrong one is the usual cause of a bill that “does not add up”: For an ordinary account charged_cents and spend_cents are the same number. They diverge on an account that is not billed, where the balance never moves and spend_cents is the only meaningful figure. by_api_key splits the same period by key, and unattributed catches work with no key behind it — the console’s own usage, and history from before key attribution existed — so the key rows plus unattributed always sum to the total. That is how you find which deployment is spending.
usage/breakdown accepts days from 1 to 365; usage/daily-breakdown accepts 1 to 30 and answers 400 unless the organization has an explicit per-second price configured. Use usage/breakdown for the general case.
Every charge is a ledger row naming the work that caused it, so a bill can always be taken apart. GET /v1/organizations/billing/credits returns them paginated, newest first, with a signed credits_delta — positive for a purchase, negative for a debit — and balance_after on each.

Things that surprise people

“You have 41 credits left” answers nothing. “About seven hours of audio left” is the same fact in the unit the work is measured in, so minutes_remaining travels on the wire beside the credit figure and price_per_minute_usd says what a credit is currently worth. What a credit buys moves with the rate card; the credit itself is always a cent.
Pay-as-you-go stops dead at zero and nothing recharges a saved card. What replaces it is a warning: when the balance falls below an hour of audio, low_balance goes true, the console shows a banner, and an email goes out — keyed per organization per hour, so a burst of work collapses to one. The flag clears only when the balance climbs back, so a top-up re-arms the warning and a long slide below it stays quiet. Watch low_balance yourself if you want to act before a 429.
A voice session debits on a one-minute beat while it is up, and the balance and free-tier day are re-checked on the same beat. So a long call cannot outrun the credit that started it, and a crash no longer loses the whole call’s minutes. Reaching a limit ends that session, with the reason named on the socket before it closes. See Pricing and limits.
Debits round to the nearest cent, and at $0.001 a minute a cent is ten minutes of audio — so an ordinary generation moves the balance by nothing. It still writes a usage row carrying the exact cost in micro-cents, which is what the consumption figures sum. A ledger that showed only balance-moving events would look empty to an account that had been working all day.
A deployment running against Stripe’s test mode returns sandbox: true on the credits response, and hosted checkout shows its own test-mode banner. Credits land in that environment’s ledger for real; the money is not.

Deployments with billing switched off

Billing is a capability, not a given. It is on only when the deployment has both Stripe credentials configured. With either missing:
  • Every billing route answers 404 Billing is not enabled on this deployment.
  • The credit gate always allows, so nothing is ever refused for quota.
  • Free-tier daily ceilings do not apply — with no plans and no purchases there is no free tier to bound, only the operator’s own hardware.
That is what a self-hosted install looks like, and what most development environments look like. If your calls succeed with a zero balance, this is why.

Next

Pricing and limits

The rate card, the grant, and every ceiling.

Sessions

The conversations behind the minutes on your bill.

Errors

What an exhausted balance looks like on each surface.

Authentication

Why the payment routes refuse an API key.