/usage),
Settings -> Billing (/settings/billing) and Settings -> Cost Tags
(/settings/cost-tags). Agent, sandbox, integration and dashboard pages show their own
usage summaries from the same queries. The plan catalog and account controls live in the
admin app (/billing, /billing/:companyId) and are a platform-admin prerequisite.
What it does
Plans. A plan is a versioned catalog row (plan_key free, pro, team,
enterprise or custom-<slug>; status draft, active, grandfathered, retired;
billing_interval month). Each plan carries a base_price_usd, a
monthly_granted_credit_usd and structural entitlements (max_agents, max_seats,
max_concurrent_dispatches, allow_oho_attach, allow_byoc, allowed_size_classes).
The plan chooser (availablePlans) lists only is_public plans in active status, in
the order free, pro, team, enterprise; the seeded Enterprise plan is not public. The
seeded version-1 catalog is:
Platform admins can publish new versions, so a deployment’s live catalog may differ.
Subscription state. Every company gets a
FREE_ACTIVE subscription when it is
created (payment_mode free). state is one of FREE_ACTIVE, PAID_ACTIVE,
PAST_DUE, SUSPENDED, DEACTIVATED. Stripe webhooks move it: a completed checkout
goes to PAID_ACTIVE, a failed invoice to PAST_DUE with a 14-day grace timer, a paid
invoice back to PAID_ACTIVE, and a deleted subscription after cancel-at-period-end back
to FREE_ACTIVE. Entering SUSPENDED starts a 60-day deactivation timer. A platform
admin can force any state with a reason.
Changing plan. createUpgradeCheckout(planKey) returns a URL the browser follows.
A first paid purchase opens a Stripe Checkout page. An existing subscriber moving to a
higher tier is upgraded in place and pays the prorated difference now. A move to a lower
tier keeps the current plan until current_period_end with no refund and shows a pending
change banner; a downgrade to Free is a cancellation at period end. Picking the current
plan while a change is pending cancels the pending change.
Wallet. The wallet has three credit classes (granted, comp, purchased),
consumed in that order. Each period’s included credit is a grant that expires at the
period end; the unspent rest is written off with an expiry entry when the next grant
lands. Free companies have no Stripe object, so a daily sweep advances their period
(anchored on the creation day, clamped to the 28th) and applies the grant. Purchased
credit has no expiry. The append-only ledger (walletLedger) records each entry with
credit_class, amount_usd, entry_type (grant, purchase, comp_grant, debit,
expiry, refund, adjustment), reason and the class balance after it.
Top-ups. createTopupCheckout(amountUsd) opens a Stripe Checkout for a one-off
credit purchase; the client offers $25, $50, $100 and $250. Credit lands only when the
checkout.session.completed webhook arrives, never on return from the page. Paid plans
only: the Free plan has no purchase button.
Auto top-up. setAutoTopup(enabled, thresholdUsd, amountUsd, monthlyCapUsd) stores a
policy: when the balance falls below the threshold after a debit, buy the amount
off-session, at most the monthly cap per calendar month and at most one attempt per 24
hours. Three consecutive card declines turn it off and email the billing contacts. The
change is written to the audit log.
Payments and receipts. billingPayments lists subscription charges (from stored
invoice.paid events, with the Stripe hosted invoice link as receipt) and top-ups (from
purchase ledger rows), newest first. createPortalSession opens the Stripe customer
portal for payment method, invoices and cancellation.
What is charged today. Only the subscription fee and top-up purchases move money.
Every LLM call runs on the company’s own provider key: resolve_llm_key_source returns
byo unconditionally, and the five-minute debit job only debits olyros_provided
interactions, so no LLM usage is debited from the wallet today. Execution time is
metered but priced at zero; the Usage page says “Not billed today — quantities only”.
The billing gates. Five enforcement points read the subscription and wallet before
work runs. Each has its own mode, off, dark (record a would-block, allow) or live
(refuse), set per deployment in the platform settings; the code default is off.
The wallet floor is $0.50 by default. Under it, a Free company reads “You’ve used this
month’s included credit. Upgrade to keep your agents running.” and a paid company “Out
of credits — top up or enable auto-top-up to keep your agents running.”
PAST_DUE keeps
full service through its grace window. Over-cap resources are never removed; only
net-new creation is refused.
Usage page. /usage has an LLM section and an execution section, a time window
(presets Today, 7 Days, 30 Days, This Month, Last Month, or a custom range) and filter
chips for actor, agent, tag and workflow. Chips of one kind are OR-ed, kinds are AND-ed,
and the window and chips live in the URL so a link reproduces the view.
detailedUsageStats returns interactions, tokens, cost, active agents, a trend against
the previous equal period, a time series (UsageGranularity HOUR for windows up to 36
hours, else DAY) and breakdowns by agent, integration, environment, model, workflow,
Slack user, actor and tag. executionUsage returns execution hours and invocations by
environment backend and by actor, split into surface classes billable_cloud
(Olyros-operated cloud), customer_hosted (the company’s OHO hosts), tenant_cloud
(the company’s own cloud project, shown at $0) and build (image builds). Export CSV
(usageExport) writes one row per model interaction, newest first, with time, agent,
environment, provider, model, tokens in and out, cost, actor and tags, restricted to the
active filters. The old
drill-down routes /usage/actor/:actorType/:actorId and /usage/tag/:tagName redirect
to the filtered main page.
Cost tags. A cost tag is a company-wide label with an optional color and
description. Tags are assigned to agents (agent and sandbox overview pages), to
workflows, and to Slack users (see Slack). When a run starts, the names of
its workflow’s or agent’s tags, plus the Slack user’s tags when the actor is a Slack
user, are copied onto the run and its interactions. Usage history therefore keeps the
tags that applied at the time; renaming or deleting a tag does not rewrite past usage.
Who can use it
- Any company member may read everything here:
billingSummary,walletLedger,billingPayments,availablePlans, every usage query (membership only), and the cost tag list and per-agent or per-workflow tags. - Company admin (ability
billing.manage, minimum role admin) is needed to change plan, buy credit, open the customer portal or set auto top-up:createUpgradeCheckout,createTopupCheckout,createPortalSession,setAutoTopup. Other members see the Billing page with these buttons disabled and the tooltip “Only workspace admins can change billing.” - Any company member may create, edit and delete cost tags and set an agent’s or a
workflow’s tags (
createCostTag,updateCostTag,deleteCostTag,setAgentTags,setWorkflowTagsall check membership only). - Company admin (ability
slack.manage) is needed to read or set a Slack user’s tags (slackUserCostTags,setSlackUserTags). - Platform admin (admin app) publishes plan versions, migrates cohorts, assigns plans, forces states, grants comps, adjusts wallets, extends grace and triggers reconciliation. Billing emails go to every active company admin, or to the company’s creator when there is none.
- Plans limit counts (agents, seats, OHO attach); no other feature is gated by plan.
How to configure it
- Choose or change a plan. Settings -> Billing (
/settings/billing) -> Upgrade or Change plan -> pick a plan -> confirm:createUpgradeCheckout(planKey). A first purchase redirects to Stripe Checkout and returns to/settings/billingwithcheckout=successorcheckout=cancelled. Cancel a scheduled downgrade with “Cancel scheduled change”, which re-picks the current plan. The plan must be synced to a Stripe price, otherwise the call fails with “Payments are temporarily unavailable”. - Buy credit. Settings -> Billing -> Wallet -> purchase -> pick an amount ->
continue:
createTopupCheckout(amountUsd). The amount must be greater than zero. - Turn on auto top-up. Settings -> Billing -> Automatic credit purchases -> enable
and fill “When balance falls below”, “Charge me” and “Up to (per month)”:
setAutoTopup. All three are required to enable; the threshold may be zero, amount and cap must be greater than zero. Disabling clears all three. - Manage the card and invoices. Settings -> Billing -> Billing & invoices:
createPortalSession. - Review spend. Settings -> Billing shows the plan, the period’s included credit
consumed, wallet balances by class, the statement (
walletLedger, page size up to 200, default 50) and payments (billingPayments). - Create cost tags. Settings -> Cost Tags (
/settings/cost-tags) -> Create Tag:createCostTag(name, color, description). Names are 1 to 100 characters of letters, digits, hyphens, underscores and spaces, unique per company ignoring case. Edit withupdateCostTag, delete withdeleteCostTag(assignments are removed with it). - Tag agents and workflows. Agents -> an agent -> Overview -> tag picker:
setAgentTags(agentId, tagIds), which replaces the agent’s set. Workflows -> a workflow -> canvas inspector (workflow settings) -> Cost Tags -> tag picker:setWorkflowTags(workflowId, tagIds), same replace semantics. At most 10 tags per agent or workflow. - Tag Slack users. Settings -> Slack Users -> tag picker:
setSlackUserTags; see Slack. - Read usage. Main -> Usage (
/usage) -> pick a window, add chips, switch breakdown tabs, Export CSV:detailedUsageStats,executionUsage,usageExport.
Limits and defaults
What it emits
- Wallet ledger rows for every grant, expiry, purchase, comp, debit, refund and adjustment, each with an audit event.
- Audit events
billing.subscription.state_changedandbilling.autotopup.updated. - A
BillingEnforcementEventfor every would-block indarkmode (gate, reason such asstate_suspended,wallet_floor,max_agents, and detail). task.billing_refusedevents on refused cloud tasks and arejected_billingrejection frame on refused sandbox invocations.- Billing emails: auto top-up disabled, and advance notice of a plan price migration.
- Cost tag names stamped on workflow executions and agent interactions, which the Usage page, the CSV export and the per-agent usage summaries read.
Known gaps
- No LLM usage is charged from the wallet: all keys are the company’s own, so the debit job writes nothing and auto top-up, which runs after a debit, never fires today.
- Execution hours are metered and shown but not priced.
allow_byoc,allowed_size_classesandmax_concurrent_dispatchesare stored on plans and shown in the chooser but nothing enforces them.- Nothing moves a company from
PAST_DUEtoSUSPENDEDwhen the 14-day grace timer runs out, or fromSUSPENDEDtoDEACTIVATEDafter 60 days; only the Stripe subscription-deleted webhook and admin force-state change these states. - The refusals
EmailUnverifiedandFreeCompanyLimitare defined but never raised. costByActorandcostByTaghave no client caller; the Usage page usesdetailedUsageStatsbreakdowns instead.- Top-ups in the payments list have no receipt link, and subscription charges from before 2026-07-07 are missing.
- A mid-period plan change does not recalculate the included credit grant.
- The Usage page has no drill-down listing the agents, workflows and Slack users that carry a given cost tag.
- The CSV export stops at 10,000 interactions without telling the user.
- Enforcement events are documented as kept for 6 months, but no job prunes them.
- There are no budgets, spend alerts or scheduled usage reports.