Docs
How it installs and what it touches. Install, the runtime contract, what leaves the machine, and fleet rollout.
Install (one machine, two minutes)
One command, no key to paste. init opens your browser to pair the machine with your workspace; confirm the code it shows and it finishes in the terminal:
npm install -g tokenoscopy tokenoscopy init
Headless, CI, or a key you'd rather manage yourself? Take one from Settings → Ingest keys and pass it:
tokenoscopy init --key tok_…
That writes hook entries to ~/.claude/settings.json (a backup of the previous file is kept, and re-running init is idempotent, and your other hooks are never touched):
- Live hooks: Claude Code POSTs session/tool events to Tokenoscopy directly. No local daemon, nothing in your request path.
- Transcript flush: on Stop/SessionEnd,
tokenoscopy flushparses the local transcript, computes spend, redacts, and uploads. This is the source of truth; a missed live hook loses nothing. - Budget guard: a PreToolUse hook asks Tokenoscopy before Bash/MCP calls. See fail-open semantics.
Verify any time with npx tokenoscopy doctor. For CI checkouts use init --project (writes ./.claude/settings.json) with TOKENOSCOPY_KEY in the environment.
Codex CLI
If Codex is installed, the same init wires it too (or ask for it: init --agent codex). It merges three things into ~/.codex/hooks.json, with a backup, leaving your own hooks alone:
- Live hooks: Codex only runs command hooks, so
tokenoscopy hookforwards each event, scrubbed, in the background. - Rollout flush: after every turn,
tokenoscopy flushreads Codex's own session file (~/.codex/sessions/…/rollout-*.jsonl), prices each model response once, and uploads it. - Budget guard: before shell,
apply_patchand MCP calls. Codex can block a call but can't ask, so a budget set to ask shows Codex a warning and lets the call run; a budget set to deny blocks it.
One step is yours. Codex won't run a new hook until you trust it: open Codex, run /hooks, and approve the tokenoscopy hooks. We never approve them for you. doctor tells you whether that's done. Until then, or on machines where you'd rather not add hooks, record from the session files alone:
npx tokenoscopy tail --agent codex --backfill --watch
Codex sessions sit beside Claude Code sessions, labelled with the agent, and the advice names Codex's own commands. The lane comes from the ChatGPT plan each session records (a plan seat), or an OpenAI API key (billed). Plans billed by usage stay unclassified rather than guessed. Prices are OpenAI's list prices, including the cache-write charge on GPT-6 and 5.6 models.
Seats and keys are not the same dollar
Tokenoscopy records both, and never adds them together. Each time a session flushes, the machine reports how it pays for tokens: metered (an API key, a Console-billed OAuth account, Bedrock or Vertex) or seat (a Pro/Max subscription). Note that signing in with OAuth does not imply a subscription. Claude Code also signs into Console accounts that bill per token, so the detector reads the plan your Claude login actually reports, and calls a machine unclassified rather than guess.
tokenoscopy doctor prints the lane it detected and the fact that decided it. If older sessions were recorded under the wrong lane, tokenoscopy tail --backfill --since 30 re-sends the last 30 days of transcripts and relabels them, safe to run any number of times. It applies the machine's current lane, since a transcript doesn't record how it was paid for.
On the metered lane, the dollar figure is real money estimated at list price, and a budget cap is literally a dollar cap. On the seat lane, the same figure is what the work would have cost at API rates. Nobody is invoiced for it. What actually runs out there is your plan's rolling usage limit, which is why a retry loop on a seat costs an afternoon rather than an invoice.
Every week-over-week comparison, headline number, and alert is scoped to one lane. If a machine can't tell (no readable config), it lands in unclassified and says so instead of guessing. A session that has only sent live hooks and hasn't flushed yet is also unclassified. Claude Code posts those hooks directly, with no Tokenoscopy process in the loop to read the local config. It resolves on the first flush, which is the end of the current turn.
The alert that fires before the bill
A budget alert tells you the money is gone. Tokenoscopy also watches for the thing that spends it next: a session where the agent repeated an identical call three or more times and edited code that runs unattended: a GitHub workflow, a cron directory, a scheduler file, or content with a timer in it.
That combination means an expensive pattern was rehearsed on one lane and written into the other. On a seat the repetition only cost capacity; running on the schedule it just wired up, against an API key, it bills every time the schedule fires, with nobody watching.
Both conditions must hold in the same session. A looser rule (same repo, same week) fires on coincidence, and a governance tool that cries wolf gets muted, which is the only failure mode that actually matters. The alert fires at most once per session and links straight to the replay.
Reading a session: which prompt was it?
A session's page leads with its diagnosis: what went wrong and what to change. Under it, where it went charges every model request and tool call to the prompt that came before it, so you can see which instruction ran up the bill, how many requests it took, and how much context each of them carried.
It also shows who started the work. Some prompts are typed by a person, some are a /command, and some are a background task finishing, which wakes the agent with nobody at the keyboard. Each of those turns carries the whole context again.
Cache rebuilds are requests that wrote 100k+ tokens of context into the prompt cache at once. Usually that is the first message after a pause long enough for the cache to expire (5 minutes or 1 hour), and the whole conversation is written again at 1.25–2× the input price. One message after a lunch break can cost more than an hour of work.
Click the tools count in the replay to list every call: by tool, with Bash broken down by command (sed -n, grep -n, …). Click a call to jump to it. Tools are shown as counts, not dollars, because not every tool call records which request issued it, and a partial figure would look like a whole one.
Capacity: pace, projection, and the alert before the cap
Every other figure in Tokenoscopy records what happened. The capacity panel on the dashboard is the one that looks forward, with the plainest arithmetic there is: the week's spend so far, the mean of the last seven days as a daily rate, and where the week and the month land if that rate holds. It is labelled a projection wherever it appears.
Vs your usual compares this week at this point (Wednesday afternoon against Wednesday afternoons) with the median of your own prior four weeks. Never a cohort, never a percentile: there is no honest one, and a ranking teaches a fleet to burn more.
Each lane has its own block and they are never added. On a seat the projected figure is capacity: what the work would cost at API rates, which nobody is invoiced for. The real limit is the plan's rolling usage window, and the pace against your own history is the earliest warning you get before hitting it.
Pace alerts. Each weekly or monthly budget card says when its cap is reached at the window's rate so far. When that date lands inside the window, after three days of data and a quarter of the cap spent, one alert goes out, days before the cap, instead of at it. Once per budget per window, by the same insert-first ledger as the threshold alerts.
Outcomes and recoverable spend
A $40 session that shipped a commit and a $40 session that left nothing used to be the same row. From CLI 0.7.0, every flush reports repo facts: HEAD, what isn't pushed, and the branch's recent commits with the files each touched. The server decides what a session left behind, never the CLI.
Shipped: a commit in the repo touched a file the session edited, whoever made the commit and whenever in the next two weeks. The human committing a minute after the agent's session ends is the common case, and the next flush of any session in that repo settles it. Drafted: the session edited files, has been quiet for an hour, and nothing was committed. Read only: it changed no lines. Sessions flushed by an older CLI stay unknown and are never counted against anyone.
The dashboard rolls outcomes up per lane and by repo, with the cost per shipped session in the lane's own unit. There is no by-developer outcome table on purpose: commits per dollar is a leaderboard, and a leaderboard teaches a fleet to commit noise.
Recoverable is the week's waste as one figure per lane: the turns that looped (one identical call repeated five times inside a turn), the turns where the developer said it was still broken, the cache rebuilds, and the drafted sessions, each a measured cost, added per session and capped at what the session cost. Recoverable means the habit has a known fix, not that the dollars come back; the finding names the largest cause and its fix.
The Flight Report, delivered
The Flight Report has always been built when you open the dashboard. Now it is delivered: every Monday at 08:00 UTC the closed week goes to the workspace's alert address, and to Slack on plans with Slack alerts. Each lane's spend in its own unit, the findings with their verdicts and what to do, the recoverable figure, and a link to the full report.
Once per workspace per week, never twice: the send claims the week before it sends, and a week that could not be sent stays on the dashboard rather than arriving stale. Switch it off in Settings; the report is still built.
The status line in Claude Code
tokenoscopy init adds a status line to Claude Code when you have none configured (a status line you chose stays yours; --no-statusline skips it). It shows this session's cost as Claude Code estimates it, with the lane named beside it so a seat never reads as a bill, the context in use, and where the session stands against its cap.
It is local only. The cap position comes from the guard's last answer, which the guard leaves in a small file beside its allow cache; the lane from this machine; the cost from the JSON Claude Code hands it. No network call, no hook, nothing that can block or slow a tool call. Every path exits 0.
Who can see whose sessions
Tokenoscopy records your engineers, not your customers, so the default posture is aggregate-first. Everyone in the workspace sees org-wide spend, the weekly report, and every rollup. Individual session replays stay with the developer who ran them, plus the workspace owner, who needs to be able to audit.
Owners can switch this off in Settings if a team has explicitly agreed to open replays. We ship it on because a tool that puts every engineer's session in front of their manager by default is a different product, and a much harder conversation with your works council or security reviewer.
What it costs you at runtime
Live hooks are async. Claude Code never waits on them. The transcript flush runs after a session ends. The only call in your critical path is the budget guard, and it's built to disappear: an allow verdict is cached locally for 15s, so a typical tool call costs about 75ms (process start), not a network round trip.
Install the CLI globally rather than relying on npx. A global binary skips package resolution on every call:
npm install -g tokenoscopy tokenoscopy init # detects the binary and wires hooks to it
What leaves the machine
Transcripts are redacted before upload, on your machine: known credential shapes (Anthropic/OpenAI/GitHub/ Slack/AWS/Google/Stripe keys), JWTs, private-key blocks, Authorization headers, connection-string passwords, KEY=value secret assignments, and email addresses in content. The server runs the same scrub as a second pass, and oversized payloads are truncated.
The replay timeline includes the agent's tool inputs, so the paths of files it read or edited are uploaded as Claude Code records them (absolute, which can include your OS username). The CLI also sends each one relative to the repo root, plus how many commits the branch is behind and ahead of its base, counted from your local git refs, with no network call and no GitHub access. That's what the Insights work map is built from. The session's repo is named by its remote owner/repo, read from the checkout the session spent most of its time in (a session started in a parent folder still lands on the repo it worked in), or the folder name when no checkout has a remote. That name is what the Projects page groups sessions by.
One third party sees text: TypeSafe (typesafe.ai), whose Jev model labels what each prompt was for, names the cause of a loop, and picks a session title from the prompts you typed. It receives each prompt's text after the redaction and truncation above, and, for a looping session, the repeated call's last three results and the agent's last two replies; and, once a week, the computed findings alongside the narrated brief, so the brief can be checked against them. It never receives file diffs, full tool output, or your API keys, and it returns only a label and a probability, never text. Every figure next to a label is still computed here. The workspace owner can switch it off in Settings; labels already made stay, new ones stop.
Costs shown everywhere are list-price estimates (the same math as Claude Code's own /usage), labeled as such until reconciled against provider billing APIs.
Budget guard: fail-open, always
The guard hook has a hard 1,500ms timeout, and every failure path allows the tool call: Tokenoscopy down, network flake, rate limit, bad response. Your session never stalls on us. Only an explicit over-budget verdict blocks (or asks), and every block is recorded on the session timeline so replays show exactly what was stopped and why.
A per-session cap counts a session's whole life, so a session resumed the next day is still over its cap and the guard keeps asking (or blocking, for a block cap) on every tool call, and the reason says when that session started. Start a new session to begin from zero. The guard judges spend as of the session's last transcript upload, so a single long turn can run past a cap before the guard sees it.
During the trial and on Solo or Team, every budget enforces. On Free, one per-session cap enforces (the oldest, if a trial left several); every other budget is still evaluated and recorded on the timeline as would have blocked, so you can see what it would have stopped.
Production: stop-limits on API spend
The guard only reaches Claude Code. For the apps and jobs that call the Anthropic API directly, wrap the client where the app creates it (once for a shared client, or at each new Anthropic()). Calls made through it are unchanged. The Production page also has a prompt you can paste into Claude Code to make this edit for you.
npm install tokenoscopy
import Anthropic from "@anthropic-ai/sdk";
import { monitor } from "tokenoscopy/anthropic";
const anthropic = monitor(new Anthropic(), { service: "checkout-api" });It reads the token counts every response already carries and uploads them with your ingest key (TOKENOSCOPY_KEY) every 10s. It sends never prompts, responses or headers, and no Anthropic credential beyond the one your app already uses. Tokenoscopy prices the counts; this is billed money, estimated at list price, and reported separately from session spend, never added to it. On a serverless function, call await flush() before returning.
A stop-limit caps one service per clock hour or UTC day. It alerts, or (only if you chose it for that limit) blocks: the wrapper checks for a block every 15s in the background and refuses the service's next call with TokenoscopyLimitError before anything is sent to Anthropic. The block lifts when the window ends or an owner resumes it. Every failure on our side (no key, no network, a timeout, a bad response, an outage) lets the call through, and a decision older than 2 minutes is ignored. Before the cap, Slack and email get a heads-up at 70% and 90% of it (once per level per window, nothing blocked). Because the wrapper counts requests, each alert can say whether there were more calls or bigger ones, which model, and which class of token.
Not in TypeScript? The wrapper speaks a small HTTP contract: POST /api/ingest/usage with { batch_id, rows: [{ service, minute, model, requests, input, output, cache_read, cache_write_5m, cache_write_1h }] } (a retried batch_id counts once), and GET /api/production/check?service=… for the current decision.
Optional: connect an Anthropic admin key in Settings to also cover keys whose code you can't change. Tokenoscopy reads usage per key every five minutes, about five minutes behind, and a key limit can switch the key off at Anthropic. Admin keys have no read-only form, so prefer the wrapper; Tokenoscopy uses one for three calls (list keys, read usage, set a key's status), encrypts it, and deletes it when you disconnect.
Fleet rollout (MDM / managed settings)
To enroll every machine without per-developer setup, render the hook block once and ship it via managed settings (macOS: /Library/Application Support/ClaudeCode/managed-settings.json). Managed policy can't be disabled by users:
npx tokenoscopy init --key $FLEET_KEY --print > hooks.json # merge hooks.json into your managed-settings.json payload
OTLP intake (Claude Code native telemetry)
For per-request spend straight from Claude Code's own telemetry (and coverage for Bedrock/Vertex deployments where no transcript flush runs), point the OTLP logs exporter at us:
CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_LOGS_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://tokenoscopy.com/api/ingest/otlp/v1/logs OTEL_EXPORTER_OTLP_HEADERS="X-Tokenoscopy-Key=tok_…"
The same spend fact arriving from several surfaces (live hook, transcript, OTLP) is deduplicated by request id. Spend is never double-counted, and the most precise source wins.
How seats are counted
A seat is a person (or an unattributed machine) that ran at least one recorded session in the billing month. It's derived from the sessions themselves, so nothing to declare and nothing to reconcile: a teammate who installs the hook becomes a seat, and someone who stops using agents stops being one.
Sessions attribute to the machine's git identity. For CI runners and containers with no readable identity, issue a labelled ingest key in Settings and its sessions land on whoever owns it.
Pricing never follows spend. Free is one developer with one per-session cap that blocks, email alerts, and 7 days of replay. Solo is $29 a month, or $290 a year, for one developer: every budget at any scope, Slack, production stop-limits and 90 days of replay. Team is custom and covers everyone. Every workspace starts with a 14-day Solo trial, no card, then drops to Free unless you subscribe; subscribing mid-trial takes the card now and charges when the trial ends (or today, if it ends within two days).
Recording is never limited by plan. A second developer in a Solo workspace is captured in full. You're pointed at Team, never silently dropped. What a plan limits is how long the detail is kept: past 7 days on Free (90 on Solo and Team) a session's step-by-step replay (prompts, tool inputs and outputs, diffs) is deleted. Its figures, lane and findings stay, and the session stays listed. For 30 days after a workspace drops to Free (a trial ending, a subscription lapsing) the 90-day window still applies, so a failed card never costs you your history overnight. Budgets Free doesn't enforce are still evaluated and record what they would have stopped.
Support
The assistant at the bottom of this page answers product questions from these docs. For anything it can't settle, early-access support runs through the founder directly. Reply to any onboarding email. Supported today: Claude Code on laptops and in CI, Codex CLI (see Codex CLI), and any app that calls Claude through the TypeScript Anthropic SDK (see Production). Roadmap: Gemini CLI parser, Cursor hooks, Codex OpenTelemetry intake, billing-API reconciliation (estimated vs billed), OTLP protobuf.