Claude Code integration
Claude Code (Anthropic's CLI) emits OpenTelemetry telemetry when the CLAUDE_CODE_ENABLE_TELEMETRY environment variable is set. Point its OTLP exporter at ActivityLog and you get per-session activity, token usage, model breakdown, and tool calls.
This is currently the only live Claude data path — there is no Anthropic-side usage import for subscription accounts. Customers on the paid Anthropic API can also forward usage via the OTel hook the same way.
What you get
| Visibility | Field on the Message row |
|---|---|
| Which model was used | model (e.g. claude-opus-4-7) |
| Input tokens | inputTokens |
| Output tokens | outputTokens |
| Cache read tokens | cacheReadTokens |
| Cache creation tokens | cacheCreateTokens |
| Cost (microcents = USD × 10⁶) | costMicrocents |
| User / session | metadata.user.id / metadata.session.id |
| Tool calls | child messages of the parent generation span |
| Prompt + completion | body (Enterprise; truncated on Pro/Free) |
This is the same gen_ai.* first-party promotion that applies to any OTLP source — see 51-ingest-otlp.md.
Setup
1. Mint a token
Portal → Systems → New System named claude-code (or whatever) → New Token → 5-year retention recommended → Generate.
2. Set environment variables
On every machine running Claude Code:
export CLAUDE_CODE_ENABLE_TELEMETRY=1
# OTLP exporter destinations
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.activitylog.com/api/v1/otlp/v1/logs
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.activitylog.com/api/v1/otlp/v1/traces
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
# Auth — bearer token on every export
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer al_REPLACE_WITH_YOUR_TOKEN"
# Identify this user / service
export OTEL_RESOURCE_ATTRIBUTES="service.name=claude-code,user.name=$USER"
Restart any Claude Code sessions to pick up the change.
3. Verify
Run any Claude Code command:
claude "what files are in this directory?"
Within a few seconds you should see new entries on the portal Messages page with serviceName: "claude-code" and type: "ai.generation" (or similar).
Cost queries
Once data is flowing, you can query cost rolled up by model / user / day:
curl "https://api.activitylog.com/api/v1/messages/aggregate?type=ai.generation&groupBy=model&metric=sum(costMicrocents)&from=2026-05-01" \
-H "Authorization: Bearer YOUR_JWT_HERE"
The Claude Code report pack (planned — see 90-reports.md) will turn these queries into a dashboard.
Multi-user setups
If multiple developers run Claude Code on their workstations and you want per-user attribution:
- Set
user.name=$USER(orOTEL_RESOURCE_ATTRIBUTES=user.id=alice@example.com) on each machine. - All sessions emit under the same token — separation is at the resource-attribute layer.
- Aggregate by
metadata.user.namein queries:?type=ai.generation&groupBy=metadata.user.name&metric=sum(costMicrocents)
If you'd prefer hard isolation (each user's data goes to a separate system), mint one token per user and configure OTEL_EXPORTER_OTLP_HEADERS accordingly. Costs are higher in admin overhead but the boundary is real.
Why not pull from the Anthropic Usage API?
The Anthropic Usage Report API is paid-API-only — it doesn't cover Claude subscription usage (Pro / Max plans). Fyin's own use of Claude Code is on subscriptions, so the OTel hook is the only signal source. For paid-API customers the Usage Report API would technically work, but the OTel path is more granular (per-tool, per-session) and avoids a polling worker.
A planned claude-usage import worker would have pulled from the Usage API; that work is shelved until a customer needs it.
Status
Live — this is the recommended setup as of 2026-05. The Fyin team uses it themselves for tracking Claude Code spend.
What's next
| Goal | Doc |
|---|---|
| The OTLP receiver behavior | 51-ingest-otlp.md |
| Query token usage and cost | 60-query-api.md |
| Claude Code report pack status | 90-reports.md |
Troubleshooting
Claude Code says telemetry is enabled but nothing arrives.
Check OTEL_EXPORTER_OTLP_HEADERS formatting — the SDK expects key1=value1,key2=value2 not standard HTTP header syntax. The leading Authorization=Bearer has no extra space inside.
Per-user costMicrocents doesn't match my Anthropic invoice.
Claude Code reports the per-request cost the local SDK calculated from its model price table. This drifts from the invoice by promotional credits, regional pricing, and rounding. It's directionally accurate — don't reconcile to the cent.