Skip to main content

Metrics Reference

This page is the consolidated reference for the metrics AxonFlow emits: what each one measures, which labels it carries, where it lives (a Prometheus counter you scrape, or a usage record you query), and which edition provides it.

Two conventions used throughout:

  • Edition. Community metrics are emitted by every edition (Enterprise deployments include all Community metrics). Enterprise metrics require an Enterprise deployment — in Community the features behind them are absent (their ingest endpoints return 501).
  • Store. Most metrics are Prometheus series scraped from the agent's and orchestrator's /prometheus endpoints. The Claude Code usage counters are different: they are stored as usage records in the platform database, not as Prometheus series. Queries and dashboards must target the right store — a PromQL query will never see a usage record, and vice versa.

Aggregate metrics vs detailed logs

Metrics answer "how much / how often" — request volume, block rates, token and cost totals, per-developer usage. They are aggregates, and Grafana is the right surface for them.

They are deliberately not the audit trail. Every individual policy decision — who asked, what was decided, which policy fired, what was redacted — is recorded as a per-decision audit record with a signed decision chain. For that detail, use the audit log and the customer portal (decisions feed, log explorer, per-decision drill-down). A useful rule of thumb: if the question contains a who or a which request, you want the portal's detailed logs; if it contains a how many or a trend, you want these metrics in Grafana.

Core runtime metrics (Community)

Emitted by the agent and orchestrator; scrape /prometheus on each. These drive the bundled Grafana dashboard.

Agent

MetricTypeLabelsWhat it measures
axonflow_agent_requests_totalcounterstatusRequests processed by the agent
axonflow_agent_request_duration_millisecondshistogramtypeAgent request latency
axonflow_agent_policy_evaluations_totalcounterPolicy-engine evaluations (an evaluation can allow or block)
axonflow_agent_blocked_requests_totalcounterRequests blocked by policy

Gateway mode

MetricTypeLabelsWhat it measures
axonflow_gateway_precheck_requests_totalcounterstatus, approvedGateway pre-check calls
axonflow_gateway_precheck_duration_millisecondshistogramPre-check policy-evaluation latency
axonflow_gateway_audit_requests_totalcounterstatus, providerGateway audit calls
axonflow_gateway_audit_duration_millisecondshistogramGateway audit latency
axonflow_gateway_llm_tokens_totalcounterprovider, model, typeLLM tokens reported through gateway audit
axonflow_gateway_llm_cost_usd_totalcounterprovider, modelEstimated LLM spend (USD) reported through gateway audit
axonflow_gateway_rbi_pii_detected_totalcounterpii_type, blockedIndia (RBI) PII detections in pre-check
axonflow_gateway_indonesia_pii_detected_totalcounterpii_type, blockedIndonesia PII detections in pre-check

Orchestrator & connectors

MetricTypeLabelsWhat it measures
axonflow_orchestrator_requests_totalcounterstatusRequests processed by the orchestrator
axonflow_orchestrator_request_duration_millisecondshistogramtypeOrchestrator request latency (routing + LLM execution)
axonflow_orchestrator_policy_evaluations_totalcounterDynamic-policy evaluations
axonflow_orchestrator_blocked_requests_totalcounterRequests blocked by dynamic policies
axonflow_orchestrator_llm_calls_totalcounterprovider, statusLLM provider API calls
axonflow_connector_calls_totalcounterconnector, operation, statusMCP connector calls
axonflow_connector_duration_millisecondshistogramconnector, operationMCP connector latency
axonflow_connector_errors_totalcounterconnector, operation, error_typeMCP connector failures

Decision Mode metrics (Community)

Emitted for POST /api/v1/decide traffic. The origin label is a closed six-value integration bucket (claude-code, claude-desktop, sdk, plugin, gateway, unknown) — see the Grafana dashboard page for how it is classified.

MetricTypeLabelsWhat it measures
axonflow_decision_requests_totalcounterverdict, stage, originDecisions by verdict (allow / deny / needs_approval), stage (llm / tool / agent) and caller integration
axonflow_decision_duration_millisecondshistogramoriginDecision handler latency
axonflow_decision_obligations_totalcounterobligation, stage, originObligations attached to an allow verdict — obligation="redact_pii" is the "redacted" signal (redaction is an obligation on allow, not a separate verdict)
axonflow_decision_blocks_totalcounterpolicy, originDeny verdicts by the single blocking policy (per-tenant custom policy ids are collapsed to tenant_custom to keep cardinality bounded)
axonflow_decision_audit_write_failures_totalcounterreasonFailures writing the decision's canonical audit record (nodb / empty_decision_id / marshal / insert) — insert or marshal is a persistence failure worth alerting on

OTLP-ingest metrics (Enterprise, v9.5.0+)

Enterprise deployments can ingest the native OpenTelemetry stream Claude Code and Claude Cowork emit — per-request events on POST /v1/logs, aggregate usage counters on POST /v1/metrics. See Claude Cowork & Claude Code OTEL Ingest for setup. In Community both endpoints return 501 — the metrics in this section only exist on Enterprise deployments.

Export-reject counter (Prometheus)

MetricTypeLabelsWhat it measures
axonflow_otel_ingest_rejected_totalcounterroute, tenant, reasonOTLP export requests rejected (status ≥ 400) at /v1/logs and /v1/metrics

This is the self-diagnosis signal for a misconfigured exporter (an OTLP client fails silently on its side). route is the ingest path; tenant is the Basic-auth org the client attempted (a bounded label set — an unauthenticated caller cannot mint unbounded series, and because it is the attempted name, corroborate a surprising spike with the agent's [OTELIngest] log lines before concluding a customer-side misconfig); reason folds the HTTP status into a fixed set: unauthorized, bad_request, forbidden, body_too_large, unsupported_media_type, rate_limited, not_implemented, storage_unavailable, server_error, client_error.

Claude Code usage counters (usage records — not Prometheus)

The usage counters that arrive on /v1/metrics are not re-exported as Prometheus series. Each accepted datapoint becomes a row in the platform's canonical usage store (usage_events, event_type = 'claude_code_metric'), keyed on the session and developer the telemetry asserts and org-tagged from the authenticated license. Stored values are delta-normalized — cumulative exports are converted to increments at ingest — so summing stored values per metric is always correct, regardless of the exporter's temporality.

The accepted metric names are a closed allowlist (anything else is rejected, visible in the export's partial_success response):

Metric nameWhat it countsStored attributes you will typically slice by
claude_code.token.usageTokens consumedtype (input / output / cacheRead / cacheCreation), model
claude_code.cost.usageEstimated cost (USD)model
claude_code.session.countCLI sessions started
claude_code.lines_of_code.countLines of code changedtype (added / removed)
claude_code.commit.countGit commits created
claude_code.pull_request.countPull requests created
claude_code.code_edit_tool.decisionClaude Code's local edit-tool permission promptsdecision (accept / reject), tool_name, language, source
claude_code.active_time.totalActive usage time (seconds)

Attributes pass a strict structural allowlist at ingest — unknown keys (including anything injected via OTEL_RESOURCE_ATTRIBUTES) are dropped before storage. Token deltas of type input / output (and cost) are additionally mirrored into the store's aggregate token/cost columns, so org-level token and cost rollups include Claude Code usage automatically; cache-token usage stays queryable via the type attribute without inflating those rollups.

Building a usage view over these records

Because these are database records, you query them with SQL (or read the surfaces that already do):

  • Customer portal — the portal's org-level usage reporting reads the same store, and its token/cost rollups include Claude Code usage automatically (via the mirrored token/cost columns). The portal aggregates by event type, not by developer — for per-developer / per-session breakdowns use the Grafana dashboard or direct SQL below.
  • Grafana — Enterprise deployments ship a dedicated Claude Code Usage dashboard (per-developer / per-session tokens, cost, lines of code, tool-permission decisions, active time, plus the export-reject counter) whose panels query the platform database through a PostgreSQL datasource. The metrics it reads land from v9.5.0; the dashboard is provisioned by the Grafana image of the first platform release after v9.5.0 (on v9.5.0 itself, import the JSON manually). See the Grafana dashboard page.
  • Direct SQL — for ad-hoc questions, sum the stored deltas. For example, tokens per developer over the last 7 days:
SELECT user_email,
SUM(metric_value) FILTER (WHERE metric_attributes->>'type' IN ('input', 'output')) AS tokens
FROM usage_events
WHERE event_type = 'claude_code_metric'
AND metric_name = 'claude_code.token.usage'
AND metric_time > NOW() - INTERVAL '7 days'
GROUP BY user_email
ORDER BY tokens DESC;

user_email and session_id are attribution labels asserted by the telemetry (see Per-Developer Identity for when a developer email is present at all); the org boundary on every row comes from the authenticated license.

Reporting role and row-level security

usage_events is row-level-secured. Query it as the table owner or a BYPASSRLS role (the bundled local datasource uses the owner). Any other role gets zero rows back silently — the RLS predicate keys on a per-session org setting that a reporting connection never sets — which is easy to misread as "no usage".

The flip side is a security property, not just a wiring detail: a reporting connection that works at all is bypassing row-level security, so it can read every org's usage records. Anything built on it — the Claude Code Usage dashboard included — shows cross-org data, and an org filter in a dashboard is a display slice, not a tenant-isolation boundary. Treat Grafana and any direct-SQL reporting access as operator-only surfaces; tenants get their own data through the customer portal, which enforces tenant scoping.

Span-derived metrics (OTel Collector overlay)

When you run the observability exporters overlay, the OTel Collector's spanmetrics connector derives RED metrics (calls_total, duration histograms) from axonflow.decision spans, with decision.verdict / decision.stage / decision.origin as dimensions. These are generated by the collector — not emitted by AxonFlow directly — and exist only where that pipeline runs.

A note on completeness

The /prometheus endpoints expose additional operational series beyond this reference (HITL queue activity, circuit-breaker state, plugin-license gauges, and other internals). They are self-describing via their HELP text and are not part of the documented stability surface; the metrics above are the ones the bundled dashboards and this documentation commit to.