Skip to Content
Polyant is open source under AGPL-3.0 — star us on GitHub.
How-toAnalyse Production Traffic

Analyse Production Traffic

On this page

The analytics endpoints answer “how much did this agent cost last week”. They do not answer “what happened last night, and did it work”. For that you need the rows, not the aggregates - and five calls get all of them.

The five calls

Every route below is a GET and is paginated. Four of them (/api/messages, /api/audit-logs, /api/ai-logs, /api/pipeline-traces) default limit to 50 (maximum 200) and take an ISO-8601 from / to window. /api/conversations is the odd one out: it defaults limit to 20 (maximum 100), and its window is updatedSince / updatedUntil, not from / to.

#RoutePermissionWhat it gives you
1/api/conversations?instanceId=&updatedSince=&updatedUntil=conversation:readwhich conversations were touched in the window
2/api/messages?instanceId=&from=&to=conversation:readevery message of that agent, across all conversations
3/api/audit-logs?instanceId=&from=&to=audit_log:readevery tool call, with its arguments, output and error
4/api/ai-logs?instanceId=&from=&to=analytics:readevery LLM call: tokens, cache breakdown, cost, callType, outcome
5/api/pipeline-traces?instanceId=&from=&to=analytics:readevery turn: total, LLM and per-phase timings

From polyant-cli, authenticated with a management key, the same five are:

polyant-cli agent conversations list acme --limit 100 --json polyant-cli agent messages acme --from 2026-08-31T18:00:00Z --limit 200 --json polyant-cli monitor events --instance acme --from 2026-08-31T18:00:00Z --json polyant-cli monitor ai-logs --instance acme --from 2026-08-31T18:00:00Z --json polyant-cli monitor traces --instance acme --from 2026-08-31T18:00:00Z --json

Join them on conversationId, which every one of the five carries.

When the question starts from one conversation

Joining after the fact is for a window. Where the subject is a single conversation, three more routes narrow to it directly, and one narrows to a single turn:

RouteParameterPermission
/api/audit-logsconversationIdaudit_log:read
/api/instances/{slug}/governance/eventsconversationId, messageIdagent.governance:read
/memoriessourceConversationIdmemory:read

/api/conversations/{id}/messages also takes around=<messageId> and returns a window centred on that message rather than the newest page, which is how a tool call in /api/audit-logs is read back in the context of the turn that issued it.

When the segment is the answer

If what you want is one row per conversation with its health, latency and cost already computed, do not compute it from these five: GET /api/conversations/export.csv streams the whole filtered segment as CSV, with the same predicates, order and columns as the conversations table, capped at 50,000 rows. The aggregates come from ai_logs, pipeline_traces, tool_audit_logs, hook_executions and governance_events - the same tables three of the routes above serve raw.

Two things that will make your numbers wrong

Failed and aborted LLM calls count toward cost, not toward call counts. The dashboard’s cost totals include every billed call, failed and pre-empted (aborted) ones included, and embedding calls. Its call counts and response times count only model calls that returned (outcome = 'ok'). The raw route returns every row and exposes the column, so filter on outcome (?outcome=ok) when you recompute counts or latencies from it.

Retention truncates the window from underneath. A global age-based sweep purges ai_logs, pipeline_traces, tool_audit_logs, hook_executions and scheduled_task_runs on their own schedule (the Analytics retention installation policy, default 90 days), independent of how long the conversations they belong to are kept. A window older than that therefore looks like conversations with no cost and no tool calls - which is missing data, not free traffic. Check the retention window in Installation policies before reading an old one; shortening it deletes history on the next housekeeping run, and the change is recorded in the management audit trail.

What is not exposed

The exact prompt sent to the provider is never returned by /api/messages. It is captured only when the agent has debug capture on, and it is read one message at a time from /api/conversations/{conversationId}/messages/{messageId}/debug, the route behind the panel’s Message details. That route also returns the turn’s model calls (modelCalls) and its channel, streaming flag and transcription (turn), with or without debug capture.

The panel’s Monitor shows tool calls, hooks, webhook conversations and scheduled runs in one timeline, but its route answers a panel session only: script against the five routes above.

Scoping

Every one of the five routes is scoped to the caller’s organization and to the workspace the request names. An agent slug belonging to another tenant returns zero rows, not an error - you cannot tell an empty agent from one you may not read, and that is deliberate.

Last updated on