Audit & stats endpoints
Read-only endpoints the SPA hits for the dashboard. All tenant-scoped.
GET /audit?limit=N
Recent decisions, newest last in the array (SPA reverses to newest-first). limit defaults to 100, max 10000.
GET /events (SSE)
text/event-stream. One data: line per new decision. Caddy is configured with flush_interval -1 so chunks aren't buffered.
GET /stats?window=N
Aggregates over the last N seconds. Default 3600.
json
{
"by_action": { "allow": 142, "redact": 8, "deny": 3 },
"by_rule": { "redact_pii_on_egress": 8, ... },
"by_agent_total": { "support-bot-v2": 1000, ... },
"by_agent_blocked": { ... },
"timeline": [ { "ts": 1715520000, "allow": 12, ... }, ... ]
}GET /vocabulary
Distinct values observed in the audit data — used to populate filter autocomplete on Live Traffic.
json
{
"agents": ["support-bot-v2", "qa_eval_loop", ...],
"actions": ["http_request", "tool_call", "llm_prompt"],
"hosts": ["api.anthropic.com", "api.openai.com", ...],
"tools": ["shell.exec", "fs.read", ...],
"models": ["claude-sonnet-4-6", "gpt-5-mini", ...]
}GET /audit/export?format=csv|json&window=N
Same data as /audit but as a streaming download with Content-Disposition set. format=csv is the format the dashboard's Export CSV button uses; format=json is the same data as the live audit endpoint.
GET /principals/summary
Per-principal-type rollups for the Spend KPI:
json
{
"user": { "count": 12, "names": [...], "cost_usd": 124.18, "tokens": 4_120_000, "calls": 18_300 },
"agent": { "count": 8, "names": [...], "cost_usd": 218.40, "tokens": 8_440_000, "calls": 21_900 },
"unknown": { "count": 0, ... }
}