Amba

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

MethodPathDescription
GET/admin/projects/:projectId/eventsCursor-paginated event log tail, newest first.
GET/admin/projects/:projectId/events/countAggregate 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

ParamTypeDefaultDescription
sinceISO-8601now − 24hLower bound on occurred_at.
untilISO-8601Upper bound on occurred_at.
event_namestringFilter to a specific event_name.
user_iduuidFilter to a single app_user.
limitint100Page size, capped at 1000.
cursorstringOpaque cursor returned as next_cursor on a previous page.

Response 200

{
  "data": [
    {
      "id": "…",
      "app_user_id": "…",
      "event_name": "workout_completed",
      "properties": { "duration_min": 30 },
      "occurred_at": "2026-04-23T14:02:11.000Z"
    }
  ],
  "next_cursor": "eyJ0cyI6IjIwMjYtMDQtMjNUMTQ6MDI6MTEuMDAwWiIsImlkIjoiLi4uIn0"
}

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_LIMITlimit is not a positive integer.
  • 400 INVALID_CURSOR — cursor is malformed.
  • 400 INVALID_FILTERuser_id is not a UUID.
  • 500 LIST_FAILED.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/events
developer auth
curl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/events'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET '${BASE_URL}/admin/projects/{projectId}/events' \
  -H 'Authorization: Bearer ${DEV_TOKEN}'

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

ParamTypeRequiredDescription
sinceISO-8601yesLower bound on occurred_at.
untilISO-8601noUpper bound on occurred_at. Defaults to now.
event_namestringnoFilter to a specific event_name.
group_byday | event_namenoBucket results by calendar day (UTC) or event_name. Omit for total only.

Response 200 (no group_by)

{ "data": { "total": 1234567 } }

Response 200 (group_by=day)

{
  "data": {
    "total": 1234567,
    "buckets": [
      { "key": "2026-04-22", "count": 412345 },
      { "key": "2026-04-23", "count": 822222 }
    ]
  }
}

Response 200 (group_by=event_name)

{
  "data": {
    "total": 1234567,
    "buckets": [
      { "key": "session_started", "count": 600000 },
      { "key": "workout_completed", "count": 420000 },
      { "key": "achievement_unlocked", "count": 214567 }
    ]
  }
}

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_SINCEsince is required.
  • 400 INVALID_SINCE / INVALID_UNTIL — unparseable timestamps.
  • 400 RANGE_TOO_LARGE — window exceeds 90 days. error.details includes max_days and requested_days.
  • 400 INVALID_RANGEuntil is before since.
  • 500 COUNT_FAILED.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/events/count
developer auth
curl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/events/count'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET '${BASE_URL}/admin/projects/{projectId}/events/count' \
  -H 'Authorization: Bearer ${DEV_TOKEN}'

On this page