Studio Backend
Usage and Billing
Conversations are billed by the minute through Stripe. The engine records how long every realtime conversation took; the platform turns those records into per-minute usage, reports it to Stripe as metered usage, and manages each account’s Stripe customer and subscription.
Accounts and Subscriptions
Every account has a BillingDetails document (billingDetails, model in promethist-common): the
Stripe customer id, the subscription id and an active flag. The customer is created together with the
account (BillingService.subscribe), named after it and tagged with the account id and this deployment’s
region (stripe.region).
The active flag matters beyond the Studio: the engine refuses conversations of an account whose
billing is not active ("Billing is not active"). The platform keeps it in step with the subscription
through the Stripe webhook. Seat-based limits that the engine applies on top come from its own pricing
records, which the platform does not manage.
What can be bought is described by Package documents (package collection): a name and description, a
default Stripe price, optional additional prices, a spend threshold and an active flag.
| Endpoint | Behavior |
|---|---|
|
Starts a Stripe Checkout session in subscription mode for a package, with success and cancel URLs.
Refused when the customer has an open or uncollectible invoice, or already has a subscription. The
subscription carries |
|
A Stripe customer-portal session for the account’s customer, which returns to the given URL. |
|
The account’s subscription: id, raw Stripe status and description. An account is expected to have at most one; more than one is reported as an error. |
|
The customer’s Stripe invoices with status, total in the currency’s minor unit, currency and the PDF and hosted-invoice links. |
|
Usage per day of the range: billed |
Stripe Webhook
POST /api/stripe/callback (StripeController, registered when stripe.webhookSecret is present) verifies
the Stripe-Signature header and handles three events for subscriptions whose region metadata matches
this deployment:
customer.subscription.created-
Applies the package of the subscription — a billing threshold when the package has a spend threshold, and its additional prices — and stores whether the subscription is active.
customer.subscription.updated,customer.subscription.deleted-
Store whether the subscription is active (
status == "active").
Other events are ignored.
The Usage Pipeline
Three scheduled jobs (util/Schedulers.kt, each triggered every minute) move usage from the engine to
Stripe. They run only when promethist.scheduling.enabled is on; the committed
default is off, so a deployment enables them in its own configuration.
-
Tasks —
createEdrProcessingTaskscreates oneusageProcessingTaskper minute, from the end of the newest existing task up to two hours ahead. A unique index on the window keeps tasks from being duplicated. -
Aggregation —
processEdrDataclaims each new task whose window ended more than 15 seconds ago and aggregates the engine’sedrrecords of typeRealtimecreated in that minute, grouped by account, project, agent and test mode. A record contributes its duration rounded up to whole minutes. The result is saved asusagedocuments in stateNew. -
Reporting —
reportUsagesToStripeclaims new usage documents one at a time and sends each as a Stripe meter event namedminutes(value = the minutes, identifier = the usage id, timestamp = the end of its window, the account’s Stripe customer id). The document endsReported, orFailedwith the reason.
Claiming is a findAndModify from New to Processing, so several backend replicas can run the jobs
without processing the same window twice. Every usage document is reported, test-mode minutes included;
the split by testMode exists in the usage report. A fourth job removes pending invitations older than
seven days every night.
In the Studio and in Claude
The billing page is shown to account Owners only. In local development the Studio skips billing and
subscription requests unless NEXT_PUBLIC_ENABLE_BILLING and NEXT_PUBLIC_FORCE_SUBSCRIPTION_CHECKS
are set, because a local backend usually has no Stripe configuration.
The MCP server can read the subscription, invoices and usage, but it has no tool that changes payment
state: checkout, portal and webhook operations are excluded by policy, and McpToolRegistryTest fails
when a tool name suggests one.