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

Analyse Production Traffic

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 the CLI 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 LLM calls are in /api/ai-logs and not in the analytics aggregates. The dashboard counts only calls that returned (outcome = 'ok'), because it measures answers. The raw route returns the failures too and exposes the column. So a total cost computed from these rows is legitimately higher than the one on the dashboard. If you want the same basis, ask for it: ?outcome=ok.

Retention truncates the window from underneath. A global age-based sweep purges ai_logs and pipeline_traces on their own schedule (ANALYTICS_RETENTION_DAYS, 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 — which is missing data, not free traffic. Check ANALYTICS_RETENTION_DAYS before reading an old window.

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.

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