Docs/Integrations/Coding Agent Telemetry

Coding-Agent Telemetry

AI coding agents emit OpenTelemetry (OTLP) data that AgenticAnts ingests into per-prompt traces — sessions, token counts, models, tool calls — attributed to the user and device. Claude Code, OpenAI Codex CLI, and GitHub Copilot CLI export OTLP natively; Gemini CLI and Cursor need an extra hop (below).

Content capture is off by default (except Gemini). Each tool ships usage metadata — counts, models, durations, tool names — out of the box. Prompt and response content only flows once you set the per-tool capture flag in the setup below. Leave the flag unset to keep prompts/responses out of the telemetry entirely.

How it works

Each tool exports OTLP over HTTP to one ingest endpoint, authenticated with your project keys. AgenticAnts detects which tool sent the batch (from the OTel service.name / instrumentation scope) and parses it into traces.

Value
Endpoint (base)https://<your-agenticants-host>/api/public/otel
Signal paths/v1/traces, /v1/logs (most SDKs append these automatically)
Protocolhttp/protobuf or http/json (both accepted)
AuthHTTP Basic — your project's public + secret API key

Replace <your-agenticants-host> with your platform host (e.g. api.agenticants.ai). Get the public (pk-...) and secret (sk-...) keys from Settings → API Keys.

Authentication

The endpoint uses standard HTTP Basic auth. Build the header value from your two keys, joined by a colon and Base64-encoded:

bash
# pk:sk -> base64 printf 'pk-...:sk-...' | base64 # -> cGstLi4uOnNrLS4uLg==

Then send it via the OTLP headers variable (or the TOML headers table for Codex):

bash
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic cGstLi4uOnNrLS4uLg=="

The endpoint authenticates only via Authorization: Basic (it needs the secret key). There are no X-Shadow-Ai-* headers — send the Base64 Basic header shown above.

Ingest flow

Rendering diagram…

User attribution

AgenticAnts attributes every trace to a user from the resource attribute user.id (also accepted as enduser.id / user.account_id). This is the one rule that matters across all five tools:

Set user.id, never rely on user.email. AgenticAnts attributes on user.id only. A resource-level user.email is intentionally ignored as an override — a stale value pushed from MDM or a shell profile must never clobber a tool's own native login. Every OTEL_RESOURCE_ATTRIBUTES example in this page uses user.id.

The tools differ in whether they self-identify the user. Claude Code and Codex stamp their own login on every span/event and AgenticAnts reads it, so a manual user.id is only an override. GitHub Copilot ships no readable user, so user.id is mandatory for a readable attribution.

ProviderNative user on the wire?Auto-attributes?How to set a readable user
Claude CodeYes — stamps user.email from your Claude login on every spanYes, automaticOptional. Set user.id in OTEL_RESOURCE_ATTRIBUTES only to override (e.g. a shared/service account).
OpenAI CodexYes — stamps user.email + user.account_id from your ChatGPT login on every eventYes, automaticOptional. Set user.id in OTEL_RESOURCE_ATTRIBUTES to override (Codex forwards the shell var to the OTLP resource).
GitHub CopilotNo — only enduser.pseudo.id, a salted, non-reversible hash (e.g. f454803c…)Only to the opaque hashRequired. Set user.id=<email> in OTEL_RESOURCE_ATTRIBUTES to get a readable user.
Gemini CLINo readable userNoSet user.id on the resource (via the collector or the launching shell).
CursorDepends on the hookNoSet user.id in the hook's environment.

Server-side fallback. The ingestion server has a global OTEL_CLI_AGENT_FALLBACK_USER that attributes any otherwise-unattributed CLI-agent trace to one user — handy for single-user / dev setups. It does not replace per-device user.id for a real fleet.


Setup

Set the values for each coding agent you use. On macOS you can set env vars session-wide with launchctl setenv; otherwise add them to your shell profile. For fleet rollout see Enterprise MDM rollout.

The OTEL_SERVICE_NAME leak. The Claude Code setup exports OTEL_SERVICE_NAME="claude-code". Any other agent launched in the same shell inherits it and gets mis-attributed (a Copilot/Codex run shows up as Claude Code). Run each agent in a fresh shell, or pin OTEL_SERVICE_NAME explicitly per tool as shown below.

Claude Code reads standard OTEL_* environment variables. Add these to your shell profile (~/.zshrc / ~/.bashrc), or on macOS set them session-wide with launchctl setenv:

bash
export CLAUDE_CODE_ENABLE_TELEMETRY=1 export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 # enables the rich span/trace view export OTEL_TRACES_EXPORTER=otlp export OTEL_METRICS_EXPORTER=otlp export OTEL_LOGS_EXPORTER=otlp export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf export OTEL_EXPORTER_OTLP_ENDPOINT="https://<your-agenticants-host>/api/public/otel" export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <base64(pk:sk)>" export OTEL_SERVICE_NAME="claude-code" export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production,user.id=<your-email>" # Content capture (prompt + tool details) — for the full trace view export OTEL_LOG_USER_PROMPTS=1 # user prompt text export OTEL_LOG_TOOL_DETAILS=1 # tool commands / names / input export OTEL_LOG_TOOL_CONTENT=1 # tool input + output (needs tracing) export OTEL_LOG_RAW_API_BODIES=1 # assistant response text

Assistant response text arrives only via OTEL_LOG_RAW_API_BODIES; extended-thinking is always redacted. To capture only usage metadata, drop the four content flags. Restart the terminal / Claude Code session so the variables are picked up.


What each tool can export

The "[… not exported by …]" text you may see in a trace is by design — it marks a field the tool's telemetry genuinely does not carry, not an ingestion bug. This matrix shows what is achievable once each tool's content flag is on.

FieldClaude CodeCopilotCodexGemini
User promptOTEL_LOG_USER_PROMPTSOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENTlog_user_promptlogPrompts (default on)
Assistant text✓ via OTEL_LOG_RAW_API_BODIES (thinking redacted)never emitted
Tool name
Tool argsOTEL_LOG_TOOL_DETAILS✓ (unconditional)
Tool resultOTEL_LOG_TOOL_CONTENT✓ (unconditional)◑ length only
Model · tokens · cost

✓ available (flag may be required) · ◑ partial · ✗ not emitted. The only permanent placeholders are Codex assistant text and Gemini tool-result bodies.


Environment variable reference

ToolKeyValue
Claude CodeCLAUDE_CODE_ENABLE_TELEMETRY1
Claude CodeCLAUDE_CODE_ENHANCED_TELEMETRY_BETA1 (enables tracing)
Claude CodeOTEL_TRACES_EXPORTER / OTEL_LOGS_EXPORTER / OTEL_METRICS_EXPORTERotlp
Claude CodeOTEL_LOG_USER_PROMPTS / OTEL_LOG_TOOL_DETAILS / OTEL_LOG_TOOL_CONTENT1 (content; unset to redact)
Claude CodeOTEL_LOG_RAW_API_BODIES1 (assistant response text)
CopilotCOPILOT_OTEL_ENABLEDtrue
CopilotCOPILOT_OTEL_ENDPOINThttps://<host>/api/public/otel
CopilotOTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENTtrue (prompt/response/tool content)
Codex(see ~/.codex/config.toml [otel] block; log_user_prompt = true, trace_exporter = "none")
All (native)OTEL_EXPORTER_OTLP_ENDPOINThttps://<host>/api/public/otel
All (native)OTEL_EXPORTER_OTLP_HEADERSAuthorization=Basic <base64(pk:sk)>
AllOTEL_SERVICE_NAMEclaude-code · github-copilot · (Codex: from resource) · cursor
AllOTEL_RESOURCE_ATTRIBUTESdeployment.environment=production,user.id=<email>

Use user.id to attribute a trace to a user on the Users page — that is the key AgenticAnts reads (user.email is ignored as an override). Claude Code and Codex auto-detect the user from their own login, so the attribute is optional for them; GitHub Copilot has no readable user, so user.id is required for it. See User attribution for the full per-provider breakdown. When the agent sets host.id=<device-uuid>, the trace also ties to a specific device.


Verify it's working

Confirm the variables are set

bash
env | grep -E "CLAUDE_CODE_ENABLE_TELEMETRY|OTEL_|COPILOT_OTEL" # shell session launchctl getenv OTEL_EXPORTER_OTLP_ENDPOINT # macOS session-wide cat ~/.codex/config.toml # Codex

Generate activity

Run a prompt in the tool (ask Claude Code a question, or run a Codex / Copilot command that calls a tool).

Check AgenticAnts

Open AgenticAnts — each prompt appears as its own trace within a minute or two, grouped into a session on the Sessions tab and attributed on Users.


Troubleshooting

Nothing shows up

  • Endpoint reachable? An unauthenticated POST should return 401 (auth required), not 404:
    bash
    curl -s -o /dev/null -w "%{http_code}\n" -X POST \ -H "Content-Type: application/json" --data '{}' \ https://<your-agenticants-host>/api/public/otel/v1/logs
    A 404 means the host/path is wrong — the base is /api/public/otel.
  • 401 errors? Check the Base64 pk:sk value and that the keys belong to the right project.
  • Wrong tool / everything shows as Claude Code? A stale OTEL_SERVICE_NAME=claude-code in the shell. Pin it per tool or use a fresh shell.

Codex floods the trace list

Set trace_exporter = "none" — the trace exporter is internal-span noise. Keep only the logs exporter with the /v1/logs path.

© 2026 ANTS Platform, Inc.Docs v1.0 · Last updated June 2026