Claude Code integration

Claude Code (Anthropic's CLI) emits OpenTelemetry when the CLAUDE_CODE_ENABLE_TELEMETRY environment variable is set. Point its OTLP log exporter at ActivityLog and you get per-session activity, tokens, cost, models and tool use per person, shown on the AI usage report and on each person's My week.

This is the live Claude data path: it works for subscription seats (Pro / Max / Team) as well as API keys, because it reports from the client, not from Anthropic's billing.

What you get

Claude Code sends one OTLP log record per activity. Each becomes one message whose type is the event name Claude Code sends, such as claude_code.api_request, claude_code.user_prompt, claude_code.tool_decision and claude_code.tool_result.

Visibility Where it lands
Who user.email → the message's actor email, so activity resolves to a person in every report
Model model column (e.g. claude-opus-4-7)
Input / output tokens inputTokens / outputTokens
Cache read / creation tokens cacheReadTokens / cacheCreateTokens
Cost (microcents = USD × 10⁶) costMicrocents, from cost_usd_micros, or cost_usd from older clients
Duration latencyMs, from duration_ms
Session session.id metadata; the AI usage report and My week count sessions by it
Tool use tool_name and decision / result on the tool records
Prompt text only with OTEL_LOG_USER_PROMPTS=1: stored in the body as prompt (encrypted at rest where your tenant has encryption on), never shown on reports, and scanned for secrets and personal data (51-ingest-otlp.md)

Every other attribute is kept as metadata on the message.

Setup

1. Create the system and token

Portal → Systems → + Mint new system. Name it so it ends in otel (for example claude-otel, or acme-otel): the AI usage report finds AI sources by that kind, and a system named claude-code would not be picked up. The token (al_…) is shown once; copy it. 5-year retention is recommended.

2. Set environment variables

On every machine running Claude Code (or in ~/.claude/settings.json under "env"):

export CLAUDE_CODE_ENABLE_TELEMETRY=1

# Send log records over OTLP/HTTP to ActivityLog
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.activitylog.com/api/v1/otlp/v1/logs

# Auth — bearer token on every export
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer al_REPLACE_WITH_YOUR_TOKEN"

# Optional: include prompt text (needed for the Compliance prompt scan)
export OTEL_LOG_USER_PROMPTS=1

ActivityLog accepts OTLP logs and traces; it does not accept OTLP metrics, so leave OTEL_METRICS_EXPORTER unset (or point it elsewhere).

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 the portal Messages page (/portal/messages?system=claude-otel) shows new claude_code.* activity, and the AI usage report shows the session.

Who is who

Claude Code attaches the signed-in user's email (user.email) to every record, so all developers can share one token and still show up as separate people. If someone's Claude account email differs from their work email, ask support to add it as an alias for that person so both resolve to one person.

If you'd prefer hard isolation (each user's data in a separate system), mint one system per user, each ending in otel.

Cost queries

The AI usage report covers cost per day, per person and per model. To pull the numbers yourself, group by any facet or column through POST /api/v1/activity/search, or read raw activity with GET /api/v1/messages?system=claude-otel.

Why not pull from the Anthropic Usage API?

The Anthropic Usage Report API covers API-key usage, not subscription seats. The OpenTelemetry path covers both and is more granular (per tool, per session) without a polling worker.

What's next

Goal Doc
The AI usage report 90-reports.md
The OTLP receiver behaviour 51-ingest-otlp.md
Query token usage and cost 60-query-api.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 HTTP header syntax. Authorization=Bearer al_… has a single space after Bearer.

Activity arrives but the AI usage report says Connect Claude Code. The system's name does not end in otel, so the report does not treat it as an AI source. Create a system named …-otel and move the token over.

Per-user cost doesn't match my Anthropic invoice. Claude Code reports the per-request cost it calculates from its own price table. This drifts from the invoice by credits, plan pricing and rounding. It is directionally accurate; don't reconcile to the cent.