Amba

Users

List and inspect end users in the tenant DB, including their streaks, entitlements, and event history.

App users live in the tenant database (app_users). These endpoints support searching, segment filtering, and drilling into a single user's gamification + entitlement + event state.

Source: apps/api/src/routes/admin/users.ts.

Endpoints

MethodPathDescription
GET/admin/projects/:projectId/usersList / search users, optionally filtered by segment.
GET/admin/projects/:projectId/users/:userIdFetch one user with streaks + entitlements.
GET/admin/projects/:projectId/users/:userId/eventsPaginated engagement-event history for a user.
GET/admin/projects/:projectId/users/exportStream all users as CSV/NDJSON.
GET/admin/projects/:projectId/users/:userId/events/exportStream a single user's events as CSV/NDJSON.
POST/admin/projects/:projectId/users/bulk-updateApply property + segment updates to up to 1000 users.

GET /admin/projects/:projectId/users

List users, newest-active first. Supports limit, offset, optional search across email / external_id / display_name (ILIKE, metacharacters escaped), and optional segment_id to filter to members of a segment.

Query

ParamTypeDefaultDescription
limitint50Page size.
offsetint0Offset.
searchstringILIKE match on email / external_id / display_name.
segment_iduuidOnly users in this segment.

Response 200

{
  "data": [ { "id": "…", "email": "…", "display_name": "…", "external_id": "…", "last_seen_at": "…",  } ],
  "total": 1234,
  "offset": 0,
  "limit": 50
}

Errors

  • 500 LIST_FAILED.

Try it:

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

Curl:

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

GET /admin/projects/:projectId/users/:userId

Fetch a single user with their current streak rows (joined to streak_definitions) and entitlements.

Response 200

{
  "data": {
    "id": "…",
    "email": "…",
    "display_name": "…",
    "anonymous_id": "…",
    "auth_providers": [{ "provider": "apple", "provider_id": "…" }],
    "properties": {},
    "first_seen_at": "…",
    "last_seen_at": "…",
    "streaks": [
      {
        "id": "…",
        "app_user_id": "…",
        "streak_definition_id": "…",
        "current_count": 7,
        "longest_count": 42,
        "status": "active",
        "streak_definitions": { "name": "Daily check-in", "qualifying_event": "check_in" }
      }
    ],
    "entitlements": [
      { "id": "…", "entitlement_id": "pro", "is_active": true, "expiration_date": "…" }
    ]
  }
}

Errors

  • 404 NOT_FOUND — user does not exist.
  • 500 FETCH_FAILED.

Try it:

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

Curl:

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

GET /admin/projects/:projectId/users/:userId/events

Paginated event history for a user, most recent first.

Query

ParamDefaultDescription
limit50Page size.
offset0Offset.

Response 200

{
  "data": [
    {
      "id": "…",
      "app_user_id": "…",
      "event_name": "workout_completed",
      "properties": {},
      "occurred_at": "…"
    }
  ]
}

Errors

  • 500 LIST_FAILED.

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events
developer auth
curl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%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}/users/{userId}/events' \
  -H 'Authorization: Bearer ${DEV_TOKEN}'

GET /admin/projects/:projectId/users/export

Stream every user in the tenant DB as CSV (default) or NDJSON. Streamed via a postgres.js cursor with batch size 500 — no full result set in memory. Each row includes id, email, anonymous_id, created_at, last_seen_at, properties, platform_strings (pipe-joined for CSV), and push_token_count.

Query

ParamTypeDefaultDescription
formatcsv | ndjsoncsvOutput format.
sinceISO-8601Optional lower bound on created_at.

Response 200

Content-Type: text/csv; charset=utf-8 or application/x-ndjson; charset=utf-8. CSV includes a header row; NDJSON emits one JSON object per line.

id,email,anonymous_id,created_at,last_seen_at,properties,platform_strings,push_token_count
…,[email protected],…,2026-04-01T12:00:00.000Z,2026-04-23T08:30:00.000Z,{"plan":"pro"},ios|android,2

Errors

  • 400 INVALID_SINCEsince is not a parseable ISO-8601 timestamp.
  • 502 TENANT_UNAVAILABLE — tenant DB validation failed before the stream opened.

Try it:

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

Curl:

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

GET /admin/projects/:projectId/users/:userId/events/export

Stream a single user's engagement events as CSV (default) or NDJSON. The user's existence is verified before the response stream commits to a 200 — a missing user returns the standard JSON error envelope.

Query

ParamTypeDefaultDescription
formatcsv | ndjsoncsvOutput format.
sinceISO-8601Lower bound on occurred_at.
untilISO-8601Upper bound on occurred_at.
event_namestringFilter to a specific event_name.

Response 200

Content-Type: text/csv; charset=utf-8 or application/x-ndjson; charset=utf-8. Columns: id, app_user_id, event_name, properties, occurred_at.

Errors

  • 400 INVALID_SINCE / INVALID_UNTIL — unparseable timestamps.
  • 400 INVALID_USER_IDuserId is not a UUID.
  • 404 NOT_FOUND — user does not exist.
  • 500 FETCH_FAILED.

Try it:

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

Curl:

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

POST /admin/projects/:projectId/users/bulk-update

Apply property merges and segment add/remove to up to 1000 users in a single transactional call. Properties are merged with jsonb concatenation (top-level merge, mirrors track() semantics). Segment add uses ON CONFLICT DO NOTHING, so it is idempotent. The whole request runs in one transaction — partial failures cannot leave a user with merged properties but missing segment tags.

Request

FieldTypeRequiredDescription
user_idsstring[]yesApp-user UUIDs (1-1000).
updates.propertiesobjectconditionalTop-level keys merged into each user's properties.
updates.add_segment_idsstring[]conditionalSegment UUIDs to add (idempotent).
updates.remove_segment_idsstring[]conditionalSegment UUIDs to remove.

At least one of properties, add_segment_ids, remove_segment_ids must be present.

Response 200

{
  "data": {
    "updated": 998,
    "failed": [{ "user_id": "…", "reason": "NOT_FOUND" }]
  }
}

failed lists user_ids that did not exist in the tenant DB; nothing is updated for those rows. The transaction still commits — non-existent users do not block the rest.

Errors

  • 400 INVALID_BODY — body is not JSON.
  • 400 MISSING_USER_IDSuser_ids empty or absent.
  • 400 INVALID_USER_IDSuser_ids contains non-strings.
  • 400 BATCH_TOO_LARGE — more than 1000 user_ids; error.details includes limit and supplied.
  • 400 EMPTY_UPDATES — none of properties, add_segment_ids, remove_segment_ids set.
  • 400 INVALID_UUID — Postgres rejected one of the supplied IDs as a malformed UUID.
  • 500 BULK_UPDATE_FAILED.

Try it:

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

Curl:

curl -X POST '${BASE_URL}/admin/projects/{projectId}/users/bulk-update' \
  -H 'Authorization: Bearer ${DEV_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{}'