Events
Project-wide engagement event log tail and aggregate counts.
These endpoints expose the raw engagement_events table — the source of truth for everything Amba.track() writes. The list endpoint is a cursor-paginated tail keyed on (occurred_at, id) so deep pages stay fast even with millions of rows; the count endpoint aggregates server-side and refuses ranges greater than 90 days.
Migration 011_event_log_index.sql adds the (occurred_at DESC, id) covering index that powers both endpoints.
Source: apps/api/src/routes/admin/events.ts.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /admin/projects/:projectId/events | Cursor-paginated event log tail, newest first. |
| GET | /admin/projects/:projectId/events/count | Aggregate event counts with optional buckets. |
GET /admin/projects/:projectId/events
Cursor pagination on (occurred_at, id) — pass next_cursor from a previous page back as cursor. The cursor is base64url-encoded JSON { ts, id }.
since defaults to 24 hours ago when omitted; the default protects the API from a query that scans the full table on an unindexed range.
Query
| Param | Type | Default | Description |
|---|---|---|---|
since | ISO-8601 | now − 24h | Lower bound on occurred_at. |
until | ISO-8601 | — | Upper bound on occurred_at. |
event_name | string | — | Filter to a specific event_name. |
user_id | uuid | — | Filter to a single app_user. |
limit | int | 100 | Page size, capped at 1000. |
cursor | string | — | Opaque cursor returned as next_cursor on a previous page. |
Response 200
next_cursor is null when the page is the last one. When the page is full (rows.length === limit), the API computes a cursor from the last row.
Errors
400 INVALID_SINCE/INVALID_UNTIL— unparseable timestamps.400 INVALID_LIMIT—limitis not a positive integer.400 INVALID_CURSOR— cursor is malformed.400 INVALID_FILTER—user_idis not a UUID.500 LIST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/eventscurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/events'Curl:
GET /admin/projects/:projectId/events/count
Aggregate counts over a time window. since is required; the API refuses ranges greater than 90 days because a full-table COUNT(*) on a high-volume tenant can be expensive. Optional group_by returns per-day or per-event_name buckets in addition to the total.
Query
| Param | Type | Required | Description |
|---|---|---|---|
since | ISO-8601 | yes | Lower bound on occurred_at. |
until | ISO-8601 | no | Upper bound on occurred_at. Defaults to now. |
event_name | string | no | Filter to a specific event_name. |
group_by | day | event_name | no | Bucket results by calendar day (UTC) or event_name. Omit for total only. |
Response 200 (no group_by)
Response 200 (group_by=day)
Response 200 (group_by=event_name)
Buckets are ordered ascending by key for group_by=day and descending by count (then key ascending) for group_by=event_name.
Errors
400 MISSING_SINCE—sinceis required.400 INVALID_SINCE/INVALID_UNTIL— unparseable timestamps.400 RANGE_TOO_LARGE— window exceeds 90 days.error.detailsincludesmax_daysandrequested_days.400 INVALID_RANGE—untilis beforesince.500 COUNT_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/events/countcurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/events/count'Curl: