> ## Documentation Index
> Fetch the complete documentation index at: https://user-docs.olyrosstudios.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing and usage

> Every workspace starts on the Free plan and can move to a paid plan from the Billing settings page.

This area covers what a company pays, what it has used and how cost is attributed. A
company is on a plan with a monthly subscription paid through Stripe, holds a prepaid
credit wallet (monthly included credit, purchased credit, platform comps), and sees its
LLM spend and execution hours broken down by agent, workflow, Slack user, actor and cost
tag. Three sidebar entries own it in the client app: **Main -> Usage** (`/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:

| Plan | Price per month | Included credit per period | Agents | Seats | OHO attach |
| - | - | - | - | - | - |
| Free | \$0 | \$5 | 2 | 1 | no |
| Pro | \$49 | \$25 | 10 | 5 | yes |
| Team | \$199 | \$100 | 50 | 25 | yes |
| Enterprise (not public) | \$0 | \$0 | unlimited | unlimited | yes |

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`.

| Gate | Where it runs | Refuses when (live) | What the user sees |
| - | - | - | - |
| `session` | every GraphQL mutation and REST write | state is `SUSPENDED` or `DEACTIVATED` | the workspace is read-only; queries still work. `SUSPENDED` companies may still run the four billing mutations to pay their way out |
| `structural` | create agent or router, invite a user, register an OHO | the plan's `max_agents` or `max_seats` is reached, or `allow_oho_attach` is false | "Your Pro plan includes 10 agents. Upgrade your plan to add more." and similar |
| `dispatch` | before a cloud task is dispatched | state is not `FREE_ACTIVE`, `PAID_ACTIVE` or `PAST_DUE`, or wallet total is under the floor | the task gets a `task.billing_refused` event and does not run |
| `gateway` | once at the start of every server-side model call | same as `dispatch` | the model call is refused with the typed message |
| `sandbox_invoke` | before a sandbox agent invocation (Slack or web) | same as `dispatch` | a rejection message in the thread (`rejected_billing`) |

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](https://user-docs.olyrosstudios.com/slack.md)). 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`,
  `setWorkflowTags` all 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.

The roles and their abilities are listed on [Company settings](https://user-docs.olyrosstudios.com/company-settings.md).

## How to configure it

1. **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/billing` with
   `checkout=success` or `checkout=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".
2. **Buy credit.** Settings -> Billing -> Wallet -> purchase -> pick an amount ->
   continue: `createTopupCheckout(amountUsd)`. The amount must be greater than zero.
3. **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.
4. **Manage the card and invoices.** Settings -> Billing -> Billing & invoices:
   `createPortalSession`.
5. **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`).
6. **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 with
   `updateCostTag`, delete with `deleteCostTag` (assignments are removed with it).
7. **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.
8. **Tag Slack users.** Settings -> Slack Users -> tag picker: `setSlackUserTags`; see
   [Slack](https://user-docs.olyrosstudios.com/slack.md).
9. **Read usage.** Main -> Usage (`/usage`) -> pick a window, add chips, switch
   breakdown tabs, Export CSV: `detailedUsageStats`, `executionUsage`, `usageExport`.

## Limits and defaults

| Setting | Default | Where enforced |
| - | - | - |
| New company subscription | `FREE_ACTIVE`, `payment_mode` `free` | company creation provisioning |
| Gate modes (`session`, `structural`, `dispatch`, `gateway`, `sandbox_invoke`) | `off` | platform settings, read by `enforce` |
| Wallet reservation floor | \$0.50 | `wallet_reservation_floor_usd` config |
| `PAST_DUE` grace window | 14 days | state machine |
| `SUSPENDED` to deactivation window | 60 days | state machine |
| Free period anchor | creation day, clamped to 28 | free grant sweep, daily 01:30 UTC |
| LLM debit job | every 5 minutes | `billing_llm_debit` system job |
| Stripe reconciliation | daily 02:15 UTC | `billing_reconciliation` system job |
| Auto top-up | off; one attempt per 24 h; disabled after 3 declines | debit job tail |
| Top-up amounts in the client | $25, $50, $100, $250 | Billing page |
| `walletLedger` `limit` | 50, clamped 1 to 200 | resolver |
| Usage `days` when no dates given | 30 | usage resolvers |
| Usage `timezone` | browser zone; unknown zones fall back to `UTC` | `resolve_timezone` |
| Usage filter lists | at most 50 values, 256 characters each; actor types `olyros_user`, `slack_user`, `webhook`, `schedule`, `agent` | `validate_usage_filters` |
| Cost tag `name` | 100 characters, unique per company ignoring case | `createCostTag`, `updateCostTag` |
| Cost tag `color`, `description` | 7 and 255 characters | model |
| Tags per agent or workflow | 10 | `setAgentTags`, `setWorkflowTags` |
| `usageExport` rows | at most 10,000, newest first | `export_rows` |

## 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_changed` and `billing.autotopup.updated`.
* A `BillingEnforcementEvent` for every would-block in `dark` mode (gate, reason such as
  `state_suspended`, `wallet_floor`, `max_agents`, and detail).
* `task.billing_refused` events on refused cloud tasks and a `rejected_billing`
  rejection 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_classes` and `max_concurrent_dispatches` are stored on
  plans and shown in the chooser but nothing enforces them.
* Nothing moves a company from `PAST_DUE` to `SUSPENDED` when the 14-day grace timer
  runs out, or from `SUSPENDED` to `DEACTIVATED` after 60 days; only the Stripe
  subscription-deleted webhook and admin force-state change these states.
* The refusals `EmailUnverified` and `FreeCompanyLimit` are defined but never raised.
* `costByActor` and `costByTag` have no client caller; the Usage page uses
  `detailedUsageStats` breakdowns 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.

## Customer-safe summary

Every workspace starts on the Free plan and can move to a paid plan from the Billing
settings page. Upgrades take effect immediately and downgrades at the end of the
current period. Each plan includes a monthly credit allowance, and paid plans can buy
extra credit or have it bought automatically under a monthly cap you set. Plans set how
many agents and team members you can have and whether you can connect your own
orchestrator. Today your agents use your own AI provider keys, so model usage is not
charged by us, and execution time is shown for information only. The Usage page breaks
your spend down by agent, workflow, person and cost tag, and you can export it as a
spreadsheet. Only workspace admins can change billing; everyone in the workspace can see
it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.