Feature Overview

Session Telemetry

Visibility into agent sessions, token usage, cost, and provenance for PRs

Summary

Connect support agent harnesses to the Zeeq telemetry endpoint to capture session-level information that can be incorporated into the dashboard and code review records.

Clients with native telemetry support:

  • Codex / ChatGPT
  • Claude Code / Claude
  • VS Code Copilot

These can be pointed directly at the Zeeq telemetry endpoint via native configuration.

Other harnesses like OpenCode, Cursor, and Pi can use OpenTelemetry plugins or directly connect to the Zeeq ingest API endpoint.

Session telemetry captures:

  • Conversation roots
  • Session prompts and MCP tool usage (it does not capture local tool usage)
  • Token usage and cost (calculated based on API rates)
  • And option to produce PR level provenance linking to a session by linking the

Configuring Codex

As of this writing, Codex can only be configured for OTEL via the root configuration at ~/.codex/config.toml. See OpenAI Advanced Config and Config Reference for details.
Your local client configuration can use the same access token for MCP and for telemetry; no need to create two. You can name the token anything you like locally.

Point your Codex client OTEL configuration to the Zeeq OTEL endpoint.

~/.codex/config.toml
[otel]
environment = "dev"
trace_exporter = "none"
log_user_prompt = true

[otel.exporter."otlp-http"]
endpoint = "https://otel.zeeq.ai/v1/logs"
protocol = "binary"

# Codex currently requires the OTLP Authorization header to be encoded directly;
# substitution will not work correctly.
# Mint this token: https://app.zeeq.ai/web/settings/credentials
[otel.exporter."otlp-http".headers]
authorization = "Bearer <ZEEQ_ACCESS_TOKEN_HERE>"

If log_user_prompt is set to true, the client will log the user prompt and the model response to the OTEL endpoint.

Codex responds with the conversation ID reliably when prompted:

Print your current conversation/session ID.

Or more precisely:

Terminal
printenv CODEX_THREAD_ID

# Or...
env | rg '^CODEX_THREAD_ID='

Configuring Claude

Claude can be configured either project local or globally; it is recommended to use project local configuration.

Create an access token

Visit https://app.zeeq.ai/web/settings/credentials and create a new API token.

Store this in your local environment as ZEEQ_ACCESS_TOKEN_HERE in your shell file (.zshrc, .bashrc, $PROFILE, etc.).

If you are already using token based auth, you can use the same header you are using for authenticating.

Configure dynamic headers

Then add a dynamic token loader in you project local .claude directory:

.claude/zeeq-otel-headers.sh
#!/usr/bin/env bash

# See: https://code.claude.com/docs/en/monitoring-usage#dynamic-headers
printf '{"Authorization":"Bearer %s"}\n' "$ZEEQ_ACCESS_TOKEN_HERE"

Ensure it is executable:

Terminal
chmod +x .claude/zeeq-otel-headers.sh
Don't forget to refresh your shell environment so the token is available to the Claude process: source ~/.zshrc or source ~/.bashrc

Configure project local settings

.claude/settings.local.json
{
  "otelHeadersHelper": ".claude/zeeq-otel-headers.sh",
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "https://otel.zeeq.ai",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_LOG_USER_PROMPTS": "1",
    "OTEL_LOG_TOOL_DETAILS": "1"
  },
  "otherConfigHere" : "..."
}
Claude does not require the /v1/logs path in the endpoint; it will automatically append it.

Like Codex, you can also set the header directly instead of using the helper:

.claude/settings.local.json
{
  "env": {
    "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer <ZEEQ_ACCESS_TOKEN_HERE>",
  }
}

This is the most straightforward way as long as .claude/settings.local.json is gitignored and not checked into source control.

Claude can respond with the conversation ID when prompted for:

Get your conversation ID from the current active scratchpad path.

VS Code Copilot

Coming soon

Direct import (Pi, Cursor, OpenCode, etc.)

Several harnesses do not have native OTEL support and must use either plugins that emulate OTEL or write a custom hook.

If preferred, for these harnesses, it is possible to use the Zeeq telemetry import API directly and send a JSON payload.

Pi

Zeeq publishes a first-party Pi extension that posts session telemetry directly to the Zeeq import API so no OTEL config or hook script required. You can check the source of the hook to write a custom extension for other harnesses.

Install the extension

The extension is published to npm as @zeeq-ai/zeeq-app-pi-telemetry. Run this from the root of the repository you want to emit telemetry from:

Terminal
pi install --local npm:@zeeq-ai/zeeq-app-pi-telemetry

To track patch releases automatically while holding the minor version, pin a tilde range instead:

Terminal
pi install --local "npm:@zeeq-ai/zeeq-app-pi-telemetry@~1.0.1"

Pi writes the package source to .pi/settings.json. Approve the project-local package so Pi trusts and loads it:

Terminal
pi --approve

Create an access token if using DCR

Visit https://app.zeeq.ai/web/settings/credentials and create a new API token if you are using dynamic client registration (DCR) for your authentication.

Store it in your shell profile (.zshrc, .bashrc, $PROFILE, etc.) as ZEEQ_ACCESS_TOKEN:

Terminal
export ZEEQ_ACCESS_TOKEN="<token>"
If you are already using token based auth, you can use the same token you are using for authenticating.
Don't forget to refresh your shell environment so the token is available to the Pi process: source ~/.zshrc or source ~/.bashrc

Start a session

No further configuration is required — the extension sends telemetry to Zeeq's hosted endpoint by default as soon as ZEEQ_ACCESS_TOKEN is set. Start Pi as normal in the trusted project and telemetry uploads automatically in the background.

Configure prompt text privacy

By default, the Pi extension exports prompt text. To omit prompt text while keeping prompt length metadata, set:

Terminal
export ZEEQ_OTEL_OMIT_PROMPT="1"

When enabled, prompt_text is exported as omit_by_config.

To keep only a prefix of each prompt, set:

Terminal
export ZEEQ_OTEL_TRUNCATE_PROMPT="32"

Truncated prompt text is exported as the retained prefix followed by ...(truncated_by_config). A value of 0, an empty value, or an omitted variable disables config truncation. Positive values below 16 are treated as 16.

Troubleshooting

Confirm the extension is installed and trusted:

Terminal
pi list

Enable export error logging so failed uploads print to stderr instead of failing silently:

Terminal
export PI_TELEMETRY_LOG_ERRORS="true"

Force an immediate upload instead of waiting for the batch timer, using the extension's own command from inside a Pi session:

/telemetry-flush
A 401 on export almost always means ZEEQ_ACCESS_TOKEN is missing, expired, or shadowed by another token exported earlier in your shell profile (later exports win). Re-mint a token at https://app.zeeq.ai/web/settings/credentials if unsure.

Raw API shape

The API endpoint for this is available at https://app.zeeq.ai/api/v1/telemetry/import and requires an Authorization: Bearer <token> header with a valid Zeeq API token.

The payload shape:

POST /api/v1/telemetry/import
{
  "conversation_id": "conv_local-session-abc123",
  "harness_name": "my-harness",
  "harness_version": "1.4.0",
  "repository_remote_url": "https://github.com/my-org/my-repo",
  "head_branch": "main",
  "head_sha": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
  "events": [
    {
      "kind": "Prompt",
      "event.timestamp": "2026-07-24T14:35:07.281Z",
      "prompt_text": "Tell me a joke",
      "prompt_length": 15,
      "gen_ai.request.model": "gpt-5.5"
    },
    {
      "kind": "ToolResult",
      "event.timestamp": "2026-07-24T14:35:08.512Z",
      "gen_ai.tool.name": "bash",
      "gen_ai.tool.call.id": "call_1",
      "gen_ai.tool.call.arguments": { "command": "ls" },
      "gen_ai.tool.call.result": "README.md\nsrc/",
      "gen_ai.tool.call.duration_ms": 42,
      "gen_ai.tool.call.success": true
    },
    {
      "kind": "Completion",
      "event.timestamp": "2026-07-24T14:35:10.208Z",
      "gen_ai.request.model": "gpt-5.5",
      "gen_ai.usage.input_tokens": 4267,
      "gen_ai.usage.cached_tokens": 0,
      "gen_ai.usage.output_tokens": 66,
      "gen_ai.usage.reasoning_tokens": 0,
      "zeeq.cost.usd": 0.023315
    }
  ]
}
  • conversation_id and harness_name are required at the top level; events must contain at least one entry.
  • harness_version, repository_remote_url, head_branch, and head_sha are optional but power PR-level provenance linking — send them when available.
  • Each event's kind is one of Prompt, ToolResult, or Completion. Send Prompt for user input, ToolResult per tool call, and Completion once per model response with token counts.
  • zeeq.cost.usd is optional. When your client can report a provider-normalized cost, send it and Zeeq treats it as authoritative; otherwise Zeeq estimates cost from token counts.
Any user.email or organization_id field sent on an event is ignored — Zeeq always attributes the request to the identity behind your bearer token.