What Otis records
Cost and revenue
How Otis measures what your AI calls cost and what each account pays, and where each number comes from.
Otis tracks two kinds of money. Cost is what your AI calls cost you: the tokens each call used, and a dollar amount for them. Revenue is what each account pays you, which your app reports.
The two come from different places and are never combined into one figure. This page explains where each number comes from and how far you can rely on it.
Purpose of cost and revenue tracking
Token usage shows where your AI spend goes: which tasks are expensive, which users are heavy, and which models carry the load. Revenue shows what each customer is worth to you. With both recorded, a question such as what a kind of work costs per paying account has the data it needs.
Token usage
For every AI call, Otis records the model, the provider, the input tokens and the output tokens. It reads these from the span that the SDK produces, and it accepts the attribute names of the Vercel AI SDK and of the OpenTelemetry GenAI conventions. Token usage lists them.
Two details affect the totals:
- Cached tokens are part of the input. Otis records tokens read from a prompt cache and tokens written to one. Both are portions of the input token count, so they are never added on top of it.
- Nested calls are counted once. An AI SDK call produces an outer span and one or more inner ones that repeat its usage. Otis leaves the repeats out of every total. If your own instrumentation reports the same usage twice, you can mark one span to be skipped. Duplicated token usage describes how.
Cost
A dollar cost comes from one of two sources.
- Reported cost. Some gateways return the amount they charged with each response. The SDK records it for the Claude Agent SDK, OpenRouter and the Vercel AI Gateway. A call made directly to OpenAI or Anthropic carries no reported cost.
- Estimated cost. Where no cost was reported, Otis estimates one from the token counts and a table of public list prices. It prices uncached input, cache reads, cache writes and output at their own rates.
Otis uses the reported cost when there is one and the estimate otherwise. It never adds the two together for the same call.
Cost estimates
- Prices are public list prices in US dollars. Otis keeps one price table for all customers and updates it daily. It doesn't know about a discount or a committed-use rate that you have negotiated, so an estimate can be higher than your bill.
- The price on the day applies. Otis doesn't store a dollar amount. It applies the price that was in force on the date of each call whenever the number is read. When Otis adds a price for a model it didn't know, earlier calls to that model get a cost too.
- An unknown model has no cost. If Otis has no price for a model, it shows the cost as unknown. It doesn't show zero. A task, session or user with any call to an unpriced model shows no cost at all, because a partial total would understate it.
Cost totals
Each task and each session carries the tokens and the cost of the AI calls it contains, with a breakdown of tokens by model.
A user's cost is added up from that user's calls, day by day. It includes only calls that carry both a user ID and a model. Calls with no user ID belong to no user, so the costs of your users don't add up to your project's total.
Revenue
Otis learns what an account pays from your app. It has no connection to a billing system. Your app reports two things:
- The plan. A
planproperty on the account, or on the user for a product sold to individuals. Your billing code can also send an event at the moment a plan changes. - The amount. Monthly recurring revenue as
mrr_minor, a whole number of minor units such as cents, with a three-lettercurrency. You can also sendseats.
Plan and revenue covers the calls, and Users, groups and accounts covers how you tell Otis which group is the account.
Otis keeps a daily record of each account's plan and amount. Four rules shape that record:
- An amount carries forward. If your app doesn't send an amount on a given day, Otis keeps the last one it received.
- Unknown is different from zero. An account with no amount ever sent has an unknown amount. An account for which your app sent zero has a known amount of zero.
- An event beats a property. When a plan change arrives both as an event and as a changed property, Otis uses the event. An event is dated when the change happened. A property change is dated on the day Otis notices it.
- Currencies are never mixed. Otis doesn't convert between currencies, and it doesn't add amounts in different currencies together.
From these records Otis computes four revenue measures: monthly recurring revenue, net revenue retention, revenue at risk, and revenue that came back from accounts that had left. Each one states the share of paying accounts that have a known amount, so that you can tell how complete it is.
Reading cost and revenue
- An estimate is a list-price figure. Treat it as a consistent way to compare tasks, users and models with one another. Your provider's invoice is the record of what you paid.
- Unknown cost means a missing price. It doesn't mean the calls were free.
- Revenue is only as current as your app makes it. Otis keeps the last amount it was told, so an account that changed plan without your app reporting it keeps its old amount.
Cost and revenue in Otis
In the data browser, the task list has Tokens and Cost columns, the session list has a Cost column, and a user's page has an AI cost card. An estimated cost is shown with a ~ in front of it, and an unknown cost is shown as a dash. Hovering over a cost says whether it was billed by the provider or estimated from list prices.
To see revenue, ask Otis in chat.
Related
- Plan and revenue covers reporting plan changes and amounts from your billing code.
- OpenTelemetry covers the usage attributes Otis reads, for apps that don't use the SDK's wrappers.
- Tasks and Sessions cover the units that cost is added up over.