All writing
5 min read

What our AI tools cost, per team

Everyone wants the AI ROI number. Finance wants cost per team. You can get it. You cannot get it clean. Attribution is the entire game, and the moment a fuzzy number is allowed to rate a person, the data dies.

I measured my own AI usage, learned what I needed, and tore it down. That was a tool for one person. The next question is the one every finance team and every VP eventually asks: what is this stuff costing us, broken down by team? And under that, the dangerous one: is it worth it?

You can answer the first. The second is a trap dressed as a spreadsheet. Here is what actually happens when you try to build the number honestly.

Every vendor reports at a different grain

Spend lands from Anthropic, OpenAI, Cursor, Copilot through GitHub, and Vertex. None of them agree on what a record is. Some give you per-user, per-day, per-model, which is the dream. Some give you a project-level total for the month with no idea which human spent it. So before you can attribute anything, you are already normalizing five different shapes into one.

That sounds like plumbing. It is, but the plumbing is where the lie gets in.

Attribution is the whole game

There are two honest ways to assign a cost.

Spend attribution maps a workspace or a project to a team through a mapping table you maintain by hand. Clean, but only as good as your discipline in keeping the map current, and useless for vendors that do not tell you who spent the money.

Roster scope counts a cost against a person's home team. Good for per-user vendors, and it forces you to define "home team" precisely so nobody gets double-counted.

For the project-only vendors, the ones with no per-user data, you are stuck estimating. The honest method is token share: this user generated 12% of the workspace's tokens this month, so assign them 12% of the workspace's cost. The word estimate is load-bearing there, and I made the code say so out loud. The field is literally null when it is unestimated, not zero, because zero is a lie and null is the truth.

Spend from five vendors - Anthropic, OpenAI, Cursor, Copilot, Vertex - drops as coins into a funnel. Filled orange coins from the per-user vendors sort cleanly into Team A, Team B, and Team C buckets. Hollow question-mark coins from the project-only vendors veer off into a separate highlighted bucket labeled '??? unattributable.' Footer: the size of 'we don't know' is itself a metric.Spend from five vendors - Anthropic, OpenAI, Cursor, Copilot, Vertex - drops as coins into a funnel. Filled orange coins from the per-user vendors sort cleanly into Team A, Team B, and Team C buckets. Hollow question-mark coins from the project-only vendors veer off into a separate highlighted bucket labeled '??? unattributable.' Footer: the size of 'we don't know' is itself a metric.
Most of it sorts into a team. The honest part is the bucket marked '???'.

The seductive next step is cost per ticket

Once you have per-person spend, the obvious move is cost per Jira ticket. I built it. Split a person's weekly spend across the tickets they committed to that week, weighted by commits. Out comes a clean dollar figure per ticket.

It is a footprint proxy, not an effort meter, and the difference is the whole point. Commits cannot tell an 800-line AI-generated diff from an 800-line file someone hand-typed. They say nothing about the day spent reading code, debugging a red herring, reviewing other people's PRs, or chasing an approach that got thrown away. The number is real arithmetic over fuzzy inputs, which is the most dangerous kind of number, because it looks precise.

So I gated it. Aggregate team and manager views only. It is structurally barred from showing up in anything individual-facing or evaluative. The fastest way to kill this data is to let it rate people, because the moment it does, people game it and you are now measuring the gaming.

A leaderboard panel titled 'AI $ / engineer' with rows eng-01 through eng-05, each a dollar bar and a figure. One row, eng-03, has spiked suspiciously with a '↑ look busy' tag. A padlock drops over the panel and an overlay reads 'GATED · team views only / never individual, never evaluative,' blurring the numbers. Footer: the moment it rates people, they game it.A leaderboard panel titled 'AI $ / engineer' with rows eng-01 through eng-05, each a dollar bar and a figure. One row, eng-03, has spiked suspiciously with a '↑ look busy' tag. A padlock drops over the panel and an overlay reads 'GATED · team views only / never individual, never evaluative,' blurring the numbers. Footer: the moment it rates people, they game it.
A per-engineer scoreboard is easy to build and the fastest way to poison the data. Lock it.

The caveats I built in, not bolted on

A few honesty mechanisms that are not optional:

  • Show gross and net side by side. Vendor "billed versus list price" moves with committed-use discounts and overage, so net is an assumption, not a fact, and it should look like one.
  • Give unattributable spend its own bucket. Do not smear it across people to make the totals tie out. The size of the "we don't know" bucket is itself a metric.
  • Track the linkage rate, the share of spend you could actually tie to tickets. It is an accounting check, not a confidence score, and conflating the two is how you end up trusting a number you should not.

What this is not

It is not a per-engineer scorecard. It is not the input to a performance review. If your version of this points a dollar figure at an individual's name in a context that can hurt them, you have not built observability, you have built a weapon, and a badly calibrated one.

The bottom line

You can get cost per team. Build it, normalize the vendors, keep the maps honest, estimate where you must and label the estimate as an estimate. Show the gross, the net, and the unknown. Then stop there.

The leak you are hunting is real and worth finding. But a fuzzy number is allowed to inform a sharp question, never to answer one about a person. Get that boundary wrong and the data does not just become useless. It becomes corrosive.