If your agency is charging you for AI work, there is a question underneath the invoice that nobody asks until a number looks wrong. Where did the figure come from? Somebody opens a session with a model, writes something, and at some point the usage data gets added up into a line on your bill. That intermediate step is where the trouble is, and it is almost always invisible from the outside.
I will not sell you the idea that measurement is hard and therefore the number has to be taken on faith. Measurement is hard, which is a reason to do it in the open, not a reason to round. What follows is how we read our own session transcripts to produce a defensible figure, and the readings we tried first that would each have overcharged a client by a factor of two or more.
If you buy agency work metered on AI usage, you will finish this piece knowing the three questions to put to the person on the other side of the invoice. If you are building the stack that produces the invoice, the arithmetic is the whole point.
A Two-Hour Budget, a Report That Said Nine Point One
The occasion was a client retainer in September. The budget was two hours of attention a month, which was ample for the account. The first weekly report, previewed before sending, claimed nine point one hours used, an overrun of seven point one on a two-hour cap. Actual human hours that month: 0.81. The number was wrong by a factor of eleven, in the direction that would have cost us the account if the preview had not caught it.
The cause was not the reporting code. The cause was the data source. The time-record endpoint we were querying returned time records and expense rows together, under the same shape, and the aggregation summed the value field on every row without checking the type. An expense of 4.80 euro was being counted as 4.80 hours. Separately, the endpoint did not return the job-type identifier, so even where tokens had their own job type in the data model, the report could not tell a token record from a human hour at read time. A value of 0.61 on a token record means units of 100,000 weighted tokens. A value of 0.61 on a human record means hours. Same column, two meanings.
We fixed the report by changing the data source and classifying job types explicitly. That is not the interesting part. The interesting part is that the failure could have gone the other way: a client-visible figure quietly accurate on average, built on primitives that silently add two kinds of work together. The 9.1 against 0.81 is a special case of a more general problem, which is that session data is easier to read incorrectly than correctly.
The Two-Times Inflator Hiding in Every Transcript
Session transcripts for AI work live as JSONL files on disk. Each line is one message, and each assistant message carries a usage object with the token counts: how many went in, how many came out, how many hit the cache, how many wrote to it. Reading them and summing the counts is a handful of lines of code and the answer looks reasonable. It is also wrong.
In the September transcripts we audited, 19,181 lines carried a usage object. Of those, 8,355 were duplicates of a message that had already appeared earlier in the same session, with the same message id. Session resumptions, replays, tool-call retries, every one of them re-emits the earlier assistant message with its usage field attached. The naive reader adds the usage field in again. Any figure computed without deduplicating on message.id was roughly two times inflated, and the inflation was invisible: the file looked long because the work was long, and the sum looked plausible because it corresponded to that length.
This is the finding that made us go back and recheck everything else. If the simplest arithmetic was off by a factor of two, every other reading we had taken on top of it was wrong by that factor too. The absolute discipline from this point on: deduplicate on message id before summing anything, with a test that re-reading the same transcript twice returns the same number.
I say this calmly now. My own reaction in the moment, cleaned up for publication, was that two short pieces of writing had somehow metered out at two hundred and eighty dollars, and the number was clearly not describing the work. The inflator was two layers deep: duplicates on top, and cache reads underneath, which is the next trap.
Cache Reads Are Not Work Delivered
The second trap is cache usage. Anthropic’s prompt caching documentation is explicit: a cache read is billed at 0.1x the base input rate for most models, and 0.05x for the current Opus and Sonnet. Writes cost 1.25x or 2x depending on cache TTL. The base model rates sit on claude.com/pricing: Opus 5.5 at $4 input and $20 output per million tokens, Sonnet 5.5 at $2 input and $10 output, as of this writing. A measurement script that treats cache_read_input_tokens as if it were an uncached input will overstate the invoice by an order of magnitude, because long sessions re-read the same context on every turn and the re-read count stacks fast.
One of our September sessions had 110 million cache-read tokens in a single transcript. Treated at list input price, that is a number you can see from space. At the actual cache-read rate, it is a different order of magnitude. The point is not that either reading is “wrong” in isolation. The point is that cache reads measure the mechanics of the session, not the artefact handed to the client. Reading the same instructions over again, backtracking, retrying a tool call, researching a dead end: those are the cost of thinking, and the client did not ask for them.
So we draw a line. The invoice is based on tokens that correspond to the delivered artefact. Input is the client’s brief or specification. Output is the content, code or document they received. Cache reads and cache writes stay on our side as overhead, the AI equivalent of office rent. The arithmetic that follows runs on the delivery base only.
One Weighted Token Instead of Two Numbers
Every current Claude model prices output tokens at exactly five times the input price. Opus 5.5 at $4 input, $20 output. Sonnet 5.5 at $2 and $10. Haiku 5.5 at $0.10 and $0.50 under one hundred thousand tokens. The ratio is consistent across the generation, which means the two numbers collapse into one:
weighted tokens = input tokens + (5 × output tokens) 1 unit = 100,000 weighted tokens
A 1,500-word article is roughly 5,000 input and 2,000 output tokens: weighted 15,000, so 0.15 units on the invoice. Convert the unit to money with the active model’s input price, stated plainly with a two-times margin that is not hidden. On Sonnet 5.5 at list, 100,000 weighted tokens cost about $0.20 to produce, so one unit lands at €0.40 to the client. On Opus 5.5 the same unit is €0.80. Absolute figures move with model and currency; the shape of the calculation does not.
The reader-facing translation that makes this legible on an invoice: one unit is roughly ten to fifteen minutes of active AI work. If a line on your bill reads “0.3 units of Opus,” that is a few minutes of model time, priced at the provider’s own billing unit and lifted onto the catalogue at a flat two-times margin. It is a figure a procurement officer can question line by line, which is the test.
What Would Be Different If We Were Starting Again
Three things, in order of how much pain they would save.
First, I would start with deduplication tests, not reporting output. A single assertion in the measurement code that re-processing the same transcript twice returns the same number would have caught the duplicates on day one. We discovered them by accident, in the diff between two consecutive readings of a session that happened to be sequential. A professional measurement stack does not rely on happening to notice.
Second, I would separate the storage of resource units from the storage of human hours at the data layer, not at the query layer. We were saving token units as “time records with a different job type,” which seemed compact at the time and silently invited every downstream reader to sum them with human hours. Expenses, which is where we migrated them to, carry their own type and cannot be confused with hours by a reader that forgets to filter.
Third, I would set a floor on micro-charges before publishing a rate card. A 25-minute speech-to-text run came out at €0.32. That is correctly computed and ridiculous on an invoice. Any catalogue item below one euro gets rounded up to one euro or bundled into a parent category. The integrity of the line-by-line approach is better served by not pushing it below the point of absurdity.
What This Means Inside a Real WooCommerce Build
Most stores do not run an AI billing stack of their own. They buy support, content, migration or build work from an agency, and some portion of that is now AI-assisted. The practical question is not how to measure your own tokens. It is what to ask about measurement when the supplier’s figure arrives.
Three questions, in order of how much ground they cover:
- Are AI consumption and human time added together anywhere in the pipeline that produces this invoice? The nine-point-one error was this exact mistake, and we found it on our own data. If a report sums every row from an endpoint that returns mixed types, the number is unverifiable from the outside.
- What is the unit on each AI line, and is it one the provider publishes, or one your supplier invented? The honest answer is Anthropic’s, OpenAI’s, Google’s, or whichever provider is actually billing. If the unit is something new, ask for the derivation and the margin applied.
- How are duplicate messages handled when the usage data is read? Any supplier metering AI from session transcripts has to answer this. If the answer is “the API gives us a number,” the number is probably two times inflated and nobody has looked.
Inside a WooCommerce estate the same questions arrive on the day you add a plugin that calls a model, or an agent that writes product descriptions. Metered usage lands in the plugin’s own ledger. The three things to look at before trusting a monthly rollup from that ledger: whether it deduplicates by message id, whether it separates cache reads from uncached input, and whether the derived line on your P&L sums kinds of work it should not. The invoice is where the mistake becomes visible. The measurement is where it starts.
Shipping this inside a store is a day of engineering at most. A single table for metered AI events keyed on the provider message id, with a unique constraint, so duplicate inserts are rejected rather than silently added. A separate column for cache-read tokens so they are logged but not billed. A nightly job that converts the day’s events into weighted-token units and writes one line per model to the store’s finance surface. The entire thing fits in two migrations and a cron.
The Boring Part Is Where the Trust Lives
Measurement is boring and shippable. You can do it right once and it keeps paying. The alternative is to let the number be whatever the first reader produced on first try, which is how a client gets a bill eleven times too large, or an agency silently absorbs a cost it should have charged for. I would rather spend a week on the arithmetic than defend a figure I cannot justify in a room with a procurement officer. If you are evaluating a supplier whose work is partly AI-metered, the three questions above are a fair thing to raise on the first call. If anything in this piece sounds like the stack producing your own invoices, we have been there and will walk through it with you.
Last modified: October 21, 2026