Spend · Subscription seats

Subscription seats

You pay for 400 ChatGPT Plus seats. Are they used, and what did they produce? Every AI tool reports API spend and none of them answer this, because a personal subscription leaves no trace in any bill. RepoOps holds both halves: the purchase record you entered, and the sessions it captured.

See it in motion

Where to find it

  • Localhost: /seat-truth.html?repo=<id>
  • API: GET /api/seat-truth?repo=<id>&window=<days>
  • Sidebar: Spend Telemetry Subscription seats
  • Keyboard: K then telemetry, then pick the Subscription seats subtab

Set it up

One half of this cannot be discovered by any machine, so you enter it. Add your subscriptions under subscriptions in the repo's account settings: vendor, plan, seat count, monthly price, currency, renewal date, and a roster of the identifiers your captured sessions carry. Price and roster are both optional. Without a price you still get the usage answer; without a roster you get the totals but no per-seat rows.

There is no credential here
A subscription row is a purchase record. Nothing in it authenticates to anything, so there is no key to leak, and a row carrying a property whose name looks like a key or a password is dropped whole rather than partly stored.

The activity half needs no setup beyond the capture you already run. This works on metadata-mode capture: knowing that a conversation happened, under which account, at which time answers the question, and no prompt or answer text is read for any of it.

What a row tells you

Sessions, conversations and last use, per seat.Sorted with the quietest seats first, so if you read only the top of the table you have read the rows a renewal conversation is actually about.
An amortized per-session figure.The monthly price divided by the seats you bought, divided again by that seat's sessions. It is blank, not 0.00, when there is nothing to divide by: a per-session figure of zero for a seat with no sessions reads as free, which is the opposite of true.
Linked commits, which read zero for now.They fill in when the paste bridge lands. Until then that zero is a real zero with a stated cause, not a measurement of nothing, and the page says so.

The claim this page refuses to make

A seat RepoOps captured nothing for is not an unused seat. The person may work through a corporate account RepoOps cannot see, have the browser extension switched off, or be on leave. A renewal decision made on a mislabelled row takes a working tool away from a real person, and they will be told a number caused it.

So such a row reads no activity captured, and carries both why it might be quiet and what would settle the question. That wording is produced in one place in the code and rendered verbatim everywhere else, and tests assert the word “unused” appears nowhere in the data, the page, or the export.

Confirm a row with the person before it changes their tools
This surface is evidence for a conversation, not a verdict to act on unread.

Three pairs of numbers that look alike

  • Seats bought is not the roster. A seat nobody was assigned is counted on its own: nobody can be using it, and nobody has been told it exists either.
  • Captured sessions is not sessions matched to a seat. The unmatched ones are reported rather than dropped, because dropping them makes every seat read quieter than the capture was.
  • The monthly price is money that left the bank. Everything derived by dividing it carries amortized in its name and never the word cost.

The renewal review

A renewal is decided in a meeting, from a file, weeks after anybody looked at a tab. The two download buttons produce that file as Markdown or CSV. It is dated, it names the window it was measured over, and it lists every seat with nothing captured plus every seat captured once or less.

The generated date and both ends of the window are the first three columns of the CSV, on every row, not a header block. A spreadsheet reader sorts, filters and pastes rows out of context within a minute of opening a file, and a seat number that has lost its period is the mistake this whole feature exists to prevent.

The dollar figure on the review is what the bill attributes to those seats. It is not what cancelling them would save, and the file says so in those words: some of them are in use through a path RepoOps cannot see, and per-seat plans rarely refund by the seat.

A partial read will not export
If either half fails to load, the page shows you which one and switches the download off. A screen can show an error beside the rows; a file gets forwarded without it, and an artifact that quietly under-reports every seat is the one that costs somebody their tool.

Honest limits

  • It reports what RepoOps captured. Coverage depends on where your team works and what capture is switched on, and a gap in coverage looks exactly like a quiet seat until you check.
  • The roster is identifiers, not people. It matches whatever string your captured sessions carry as a user id. A person whose seat is registered under a different identifier reads as two facts: a quiet seat, and unmatched sessions.
  • Rosters go stale. Seats bought and seats rostered are kept separate for that reason, and the difference is shown rather than reconciled for you.
  • A subscription with no price answers usage only. No price means no per-seat figure, and the page says “not stated” rather than 0.00.

Read more

  • Activity ledger: per-developer session timing from Claude Code telemetry.
  • Metered spend: real metered LLM spend, which a flat subscription is not.

Last updated