Runtimes
OpenTelemetry ingest#
ClawMetry speaks OTLP in both directions. Anything that can emit OpenTelemetry can push into it, and ClawMetry can push its own telemetry out to the stack you already run.
This is the supported path for runtimes with no local store — n8n backed by Postgres, a hosted CI agent, a framework with native OTel support, or your own service.
Receiving#
Three HTTP endpoints on the dashboard:
| Endpoint | Payload |
|---|---|
POST /v1/traces | Spans — the agent's structure, tool calls, latencies |
POST /v1/metrics | Counters and gauges — tokens, cost, run counts |
POST /v1/logs | Log records — including cost-bearing events from runtimes that report cost as a log |
Point any OTLP/HTTP exporter at http://localhost:8900:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8900
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufInstall the optional protobuf dependency to accept binary OTLP:
pip install 'clawmetry[otel]'Authentication#
OTLP ingestion accepts untrusted data that lands in your store, so it is authenticated like any other write surface. For a local-only setup where the receiver is not reachable off-box:
CLAWMETRY_OTLP_ALLOW_UNAUTH=1 clawmetryDo not set that on anything with a routable address.
Limits#
| Variable | Default | Purpose |
|---|---|---|
CLAWMETRY_OTLP_MAX_DECOMPRESSED_MB | 64 | Reject payloads that expand beyond this. Guards against decompression bombs. |
CLAWMETRY_OTLP_APP_CAP | — | Cap on distinct applications accepted, so one misconfigured exporter cannot fill the store. |
CLAWMETRY_MAX_REQUEST_MB | — | Global request size cap. |
Claude Code's native OTel#
Claude Code emits OpenTelemetry natively, and ClawMetry consumes it alongside the JSONL transcripts. Two things are worth knowing if you wire it up:
- Cost arrives on both a log record and a metric. ClawMetry counts it once.
If you build your own pipeline on the same feed, do not sum both.
- Session ids appear bare on the wire, not namespaced with a runtime
prefix, so anything joining OTel spans to sessions has to know that.
Spans map onto ClawMetry's model directly: an assistant turn becomes an
llm.call, each tool_use a tool.<name> child, a Task an agent.spawn,
and a thinking block an internal thinking span. That mapping is what the
Agent Graph and turn-anatomy views are built on.
Querying what arrived#
# traces, one row per trace_id with aggregate span stats
curl -s 'http://localhost:8900/api/local/traces?limit=20' | jq '.rows'
# spans, filtered
curl -s 'http://localhost:8900/api/local/spans?session_id=<sid>&limit=200' | jq '.rows'
# a single span
curl -s 'http://localhost:8900/api/local/spans/<span_id>' | jqThe Tracing tab renders the same data as a waterfall.
Exporting out#
ClawMetry can push its own metrics to your collector, so agent cost and activity land next to the rest of your infrastructure telemetry.
| Variable | Purpose |
|---|---|
CLAWMETRY_OTEL_EXPORT_ENDPOINT | OTLP endpoint to push to. Setting it starts the exporter. |
CLAWMETRY_OTEL_EXPORT_HEADERS | JSON object of headers, for auth against your collector |
CLAWMETRY_OTEL_EXPORT_INTERVAL | Export cadence in seconds |
export CLAWMETRY_OTEL_EXPORT_ENDPOINT=https://otlp.example.com/v1/metrics
export CLAWMETRY_OTEL_EXPORT_HEADERS='{"x-api-key":"…"}'
export CLAWMETRY_OTEL_EXPORT_INTERVAL=60Status and a manual trigger:
curl -s localhost:8900/api/otel-status | jq
curl -s localhost:8900/api/otel/export | jqTier: the export path is otel_export, a Pro feature. Ingestion is not
gated.
Which path should you use?#
| You have | Use |
|---|---|
| A framework with OTel support | This page — point the exporter at ClawMetry |
| Python code you can edit | The interceptor — two lines, gets cost |
| Structured runs to push | The ingest API — explicit run and event shape |
| A runtime that writes to disk | Ask for an adapter — it is the highest-fidelity path |