Skip to content
GitHub
Get started →

Analytics endpoint

Every session, every function call, every database query is tracked as a UsageEvent. The analytics endpoint lets you query them — either for billing reconciliation, BI dashboards, or debugging live behavior.

Endpoints

MethodPathScope
POST/v1/:siteId/analytics/eventWritten by the widget itself — you don’t call this
GET/v1/:siteId/analytics/eventsanalytics:read
GET/v1/:siteId/analytics/summaryanalytics:read

UsageEvent shape

type UsageEventType =
| 'session_start'
| 'session_end'
| 'function_call'
| 'db_query'
| 'image_description';
interface UsageEvent {
id: string;
site_id: string;
type: UsageEventType;
duration_seconds?: number; // session_end only
function_name?: string; // function_call only
cost_cents?: number; // computed post-hoc
metadata?: Record<string, unknown>;
created_at: string;
}

GET /v1/:siteId/analytics/events

List raw events, paginated.

GET /v1/ab1c2d3e/analytics/events?since=2026-04-01&type=session_end&limit=100
Authorization: Bearer vk_live_...

Query params:

ParamDefaultNotes
since30 days agoISO date
untilnowISO date
typeallFilter by UsageEventType
limit100Max 1000
cursorOpaque pagination cursor

Response 200:

{
"success": true,
"data": [
{
"id": "evt_abc",
"site_id": "ab1c2d3e",
"type": "session_end",
"duration_seconds": 124,
"cost_cents": 42,
"metadata": { "session_id": "sess_xyz", "function_calls": 7 },
"created_at": "2026-04-17T14:22:10.000Z"
}
],
"meta": {
"next_cursor": "cur_xyz"
}
}

GET /v1/:siteId/analytics/summary

Pre-aggregated metrics for dashboards.

GET /v1/ab1c2d3e/analytics/summary?period=month&bucket=day
Authorization: Bearer vk_live_...

Query params:

ParamValuesDefault
periodday, week, month, quarter, year, custommonth
buckethour, day, weekday
since / untilISO dates (with period=custom)

Response 200:

{
"success": true,
"data": {
"totals": {
"sessions": 1247,
"minutes": 3128,
"function_calls": 4821,
"db_queries": 2103,
"cost_cents": 18450
},
"buckets": [
{
"bucket": "2026-04-01",
"sessions": 38,
"minutes": 94,
"function_calls": 142,
"db_queries": 61,
"cost_cents": 561
},
...
],
"top_functions": [
{ "name": "search_database", "count": 2103 },
{ "name": "navigate", "count": 1234 },
{ "name": "scroll_to", "count": 892 }
]
}
}

POST /v1/:siteId/analytics/event (widget only)

The widget writes events as they happen. You don’t call this endpoint directly.

POST /v1/ab1c2d3e/analytics/event
Origin: https://emberandoak.com
Content-Type: application/json
X-Spelo-Session: sess_xyz
X-Spelo-Signature: sha256=...
{
"type": "function_call",
"function_name": "navigate",
"metadata": { "url": "/menu" }
}

Rate-limited to 600/minute per site.

Real-time streaming (beta)

For live dashboards, subscribe to events via Server-Sent Events:

GET /v1/:siteId/analytics/stream
Authorization: Bearer vk_live_...
Accept: text/event-stream

The endpoint emits one SSE event per UsageEvent as it’s recorded. Useful for a live “current sessions” widget in your ops dashboard.

Cost computation

  • session_end.cost_cents is filled in asynchronously (~1 minute after session ends) once OpenAI’s usage webhook confirms actual token consumption.
  • For managed plans, this is what counts against your bundled minutes.
  • For BYOK, this is informational — OpenAI bills you directly.

Retention

  • Raw events: 13 months
  • Aggregated summaries: indefinite

Export to your own warehouse for longer retention — see Webhooks.

Error codes

HTTPCodeCause
400invalid_paramsBad date format or unknown type
401unauthorizedMissing / revoked API key
403forbiddenKey lacks analytics:read scope
404site_not_foundUnknown site_id

See also