How attribution works
Panorama ties every request to a work item: a branch, a pull request or a ticket. That is what turns “we spent $4,200 on AI this month” into “PAY-1183 cost $18.40, and it merged”.
Nothing is installed or configured on the engineer’s machine for this. A coding tool already sends its working context with every request, and Panorama reads it on the way past.


Where the answer comes from
Section titled “Where the answer comes from”There are four sources, tried in this order. A higher one always wins.
| Source | What it is | Confidence |
|---|---|---|
| Stamp | Headers the client sends on purpose, naming the repository, branch, ticket or pull request. | Exact |
| Structural | Read from the shape of the request: the tool’s environment block, git and gh output in tool results, file paths, remotes. |
High |
| Content | A ticket or pull request key mentioned in the conversation. Used only when the session backs it up. | Low |
| Unattributed | Nothing could be found. | Shown as such |
The source is recorded with every attribution and shown beside it, for example “PAY-1183, from the branch name (structural, high)”.
Attribution is decided once per session and inherited by every request in it. It is not re-guessed per request. If better evidence turns up later in the session, the whole session moves to the better answer.
opencode and Claude Code name their own sessions. Each sends a session id with every request, so their turns are grouped exactly rather than inferred. opencode’s subagents are grouped with the conversation that started them, and Claude Code’s background calls to its fast model, which carry no repository of their own, are grouped with the conversation they were made for.
A branch name carries the ticket. A branch called feat/PAY-1183-refunds
is attributed to ticket PAY-1183 from the name alone. No tracker integration
is needed for that.
What each tool gives you
Section titled “What each tool gives you”| Tool | Reaches | Why |
|---|---|---|
| Claude Code | Branch and ticket | Its environment block includes the current branch. |
| opencode | Repository only | It sends its working directory and no branch. |
| Codex CLI | Not measured yet | It is attributed by the same rules, and the end-to-end tests do not cover it yet. |
Both of the first two rows are checked by the end-to-end test suite against the real tools, so a tool changing what it sends shows up as a failing test.
To get exact attribution from Claude Code, add the optional stamping step on
My setup. It sets ANTHROPIC_CUSTOM_HEADERS from your git checkout. See
Claude Code.
Reading the gap
Section titled “Reading the gap”

Some spend cannot be attributed, and Panorama says how much and why. On Analytics → Attribution, every unattributed request falls into exactly one of three reasons:
- no repository was open,
- a repository was open and there was no branch,
- there was a branch, and its name matched no work item.
The three add up to the unattributed total exactly. Exploratory sessions outside a repository are the usual cause, and that is normal.
If the gap is large:
- Check which tools people use. opencode traffic stops at the repository.
- Check your branch naming. A ticket key in the branch name is what links
a branch to a ticket. If your keys do not look like
ABC-123, setattribution.ticket_patternin the configuration file. - Consider stamping for pipelines and other traffic that has no checkout.
Confirmation against GitHub
Section titled “Confirmation against GitHub”The GitHub integration is experimental: it has not yet been tested against a real GitHub organization.
Once GitHub is connected, Panorama asks it about the branches and pull requests it has already seen in traffic, and about nothing else. That adds titles, authors and whether the work merged, which is what cost per merged pull request is built from.
Confirmation never changes an attribution. A branch GitHub has never heard of may simply not have been pushed yet.
Related
Section titled “Related”- Cost centers allocate attributed spend to an accounting line.
- Reading request bodies covers how the extractors work and what they had to learn.
This manual is licensed underCC BY 4.0. Code samples in it are part of Panorama, licensed under theGNU AGPL v3.0 only.