Usage & Analytics API
The hosted API records per-turn token and cost data. These endpoints are served
by @mordn/chat-api, not by your Next.js handler, and require a server-side
mordn API key.
The key fixes tenant and agent scope
The API key resolves both tenant and agent. Callers cannot select another agent with a query parameter. Never expose the key to the browser.
curl "https://api.mordn.com/v1/usage/summary?view=production" \
-H "Authorization: Bearer $MORDN_CHAT_KEY"Window and filters
The default window is the last 14 days. from is inclusive and to is
exclusive. Both are ISO timestamps. view is production, preview, or all
(the default).
Filters differ by endpoint:
| Endpoint | Additional filters |
|---|---|
summary | userId, model, view |
timeseries | `bucket=day |
by-model | userId, view |
by-user | limit, model, view |
top-conversations | limit, `sort=cost |
unanswered | optional consistency-check agentId |
Endpoints
GET /v1/usage/summary
{
"totals": {
"events": 12840,
"inputTokens": 8340221,
"outputTokens": 2114880,
"totalTokens": 10455101,
"cachedInputTokens": 512000,
"cacheReadTokens": 0,
"cacheWriteTokens": 0,
"costUsd": "42.1187",
"inputCostUsd": "30.0000",
"outputCostUsd": "12.1187",
"marketCostUsd": "40.0000",
"surchargeUsd": "2.1187"
}
}GET /v1/usage/timeseries?bucket=day
{
"series": [
{
"ts": "2026-06-30T00:00:00.000Z",
"events": 502,
"inputTokens": 320000,
"outputTokens": 90233,
"totalTokens": 410233,
"costUsd": "1.6644"
}
]
}GET /v1/usage/by-model
Returns { models: [{ model, events, totalTokens, costUsd }] } ordered by cost.
GET /v1/usage/by-user
Returns { users: [{ userId, events, totalTokens, costUsd }] }.
GET /v1/usage/top-conversations
Returns { conversations: [{ conversationId, events, totalTokens, costUsd }] }.
The aggregate does not include userId.
GET /v1/usage/unanswered
Returns recent retrieval misses grouped by normalized query:
{
"questions": [
{
"query": "How do I rotate a key?",
"count": 7,
"lastAskedAt": "2026-07-14T08:00:00.000Z",
"avgTopScore": 0.08
}
]
}An optional agentId must match the key's agent or the API returns 403.
Because this endpoint returns end-user query text, apply the same access and
retention controls as other analytics data.
Per-turn record
UsageRecord can also carry resolvedProvider, finish reason, step count,
latency, generation id, exact decimal cost strings, and
raw?: Record<string, unknown> provider metadata. Not every field is exposed by
every aggregate endpoint.
For a self-hosted backend, use onChatFinish or persist SaveTurnInput.usage
in your own store.