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
| Method | Path | Description |
|---|---|---|
| GET | /admin/projects/:projectId/users | List / search users, optionally filtered by segment. |
| GET | /admin/projects/:projectId/users/:userId | Fetch one user with streaks + entitlements. |
| GET | /admin/projects/:projectId/users/:userId/events | Paginated engagement-event history for a user. |
| GET | /admin/projects/:projectId/users/export | Stream all users as CSV/NDJSON. |
| GET | /admin/projects/:projectId/users/:userId/events/export | Stream a single user's events as CSV/NDJSON. |
| POST | /admin/projects/:projectId/users/bulk-update | Apply 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
| Param | Type | Default | Description |
|---|---|---|---|
limit | int | 50 | Page size. |
offset | int | 0 | Offset. |
search | string | — | ILIKE match on email / external_id / display_name. |
segment_id | uuid | — | Only users in this segment. |
Response 200
Errors
500 LIST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/userscurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users'Curl:
GET /admin/projects/:projectId/users/:userId
Fetch a single user with their current streak rows (joined to streak_definitions) and entitlements.
Response 200
Errors
404 NOT_FOUND— user does not exist.500 FETCH_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7Dcurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D'Curl:
GET /admin/projects/:projectId/users/:userId/events
Paginated event history for a user, most recent first.
Query
| Param | Default | Description |
|---|---|---|
limit | 50 | Page size. |
offset | 0 | Offset. |
Response 200
Errors
500 LIST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/eventscurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events'Curl:
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
| Param | Type | Default | Description |
|---|---|---|---|
format | csv | ndjson | csv | Output format. |
since | ISO-8601 | — | Optional 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.
Errors
400 INVALID_SINCE—sinceis not a parseable ISO-8601 timestamp.502 TENANT_UNAVAILABLE— tenant DB validation failed before the stream opened.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/exportcurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/export'Curl:
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
| Param | Type | Default | Description |
|---|---|---|---|
format | csv | ndjson | csv | Output format. |
since | ISO-8601 | — | Lower bound on occurred_at. |
until | ISO-8601 | — | Upper bound on occurred_at. |
event_name | string | — | Filter 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_ID—userIdis not a UUID.404 NOT_FOUND— user does not exist.500 FETCH_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events/exportcurl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/%7B%7BuserId%7D%7D/events/export'Curl:
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
| Field | Type | Required | Description |
|---|---|---|---|
user_ids | string[] | yes | App-user UUIDs (1-1000). |
updates.properties | object | conditional | Top-level keys merged into each user's properties. |
updates.add_segment_ids | string[] | conditional | Segment UUIDs to add (idempotent). |
updates.remove_segment_ids | string[] | conditional | Segment UUIDs to remove. |
At least one of properties, add_segment_ids, remove_segment_ids must be present.
Response 200
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_IDS—user_idsempty or absent.400 INVALID_USER_IDS—user_idscontains non-strings.400 BATCH_TOO_LARGE— more than 1000 user_ids;error.detailsincludeslimitandsupplied.400 EMPTY_UPDATES— none ofproperties,add_segment_ids,remove_segment_idsset.400 INVALID_UUID— Postgres rejected one of the supplied IDs as a malformed UUID.500 BULK_UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/users/bulk-updatecurl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/users/bulk-update'Curl: