Users Domain
The Users domain provides endpoints for managing users within a client organization. Users are identified by a stable id and a system-managed username and user_key. Activity timestamps reflect the last time a user was seen across any channel (web or desktop).
For embedding and endpoint design decisions, see the Design Guidelines.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| List Users | GET | Paginated list of users. Use for tables, dropdowns, and search. |
| Get User | GET | Fetch a single user directly by ID. Use for profile pages, post-update refresh, and cross-domain links. |
| User Summary | GET | Active vs stale user counts with configurable staleness threshold. |
| Update User | PATCH | Update a single user's department assignments and status. |
| Bulk Update User Status | PATCH | Update status for up to 200 users in one request, with per-user success/failure results. |
| Bulk Archive Users | POST | Archive collection for multiple users (reversible). |
| Bulk Activate Users | POST | Reactivate collection for multiple archived users. |
| Bulk Delete Users | POST | Soft-delete multiple users (irreversible). |
When to use List vs Get User
| Use case | Endpoint |
|---|---|
| Render a user table with departments | GET /users |
Dropdown or autocomplete (?q=) | GET /users |
| Filter users by one or more departments | GET /users?department_id= |
| Load a user profile page by ID | GET /users/{id} |
| Refresh one user after an update | GET /users/{id} |
| Another domain links to a user (e.g. device → user) | GET /users/{id} |
Getting Devices by User
Devices are not embedded in user responses because a user can use many devices over time. Use the List Devices endpoint with the user_id filter:
GET /clients/{client_id}/devices?user_id=a1d97031-04e2-4907-a249-093f7436207b
DTO Hierarchy
UserRef (identity + status + activity -- no joins)
└── User (UserRef + departments + audit -- requires join)
UserRef is defined here but consumed by other domains (e.g. DeviceUserRef). User is what all user-primary endpoints return.
Data Transfer Objects (DTOs)
UserRef
User reference for embedding inside other domain responses. Think of it as a business card -- enough to identify the user, display their name, and show when they were last active. Contains all fields directly on the users table that are useful in a reference context. No joins required.
The rule for what belongs in UserRef: ask "does every place this user is referenced as a non-primary resource need this field?" If yes, it belongs here. If only the user's own endpoints need it, it belongs in User only.
{
"id": "a1d97031-04e2-4907-a249-093f7436207b",
"username": "mikechang",
"user_key": "mikechang",
"status": "active",
"last_activity": {
"at": "2026-02-24T21:23:53.082Z",
"desktop_at": "2026-02-24T21:22:46.119Z",
"web_at": "2026-02-24T21:23:53.082Z"
}
}
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (uuid) | No | Stable unique identifier for the user |
username | string | No | System-managed username |
user_key | string | No | Unique key used to identify the user within the client (e.g. email or SSO subject) |
status | string | No | User collection status: active, archived, deleted. Default: active. See Collection Control. |
last_activity | object | No | Grouped activity timestamps across all channels |
last_activity.at | string (ISO 8601) | Yes | Most recent activity across any channel |
last_activity.desktop_at | string (ISO 8601) | Yes | Most recent desktop agent activity |
last_activity.web_at | string (ISO 8601) | Yes | Most recent web activity |
DB source: users.id, users.username, users.user_key, users.status, users.last_activity_utc, users.last_desktop_activity_utc, users.last_web_activity_utc
status belongs on UserRef because it satisfies all three embedding-rule conditions: bounded (single value), always needed for display (archived badge in UI), and static reference data (stored directly on users table, no join).
Used in: Embedded inside other domain responses where a user is referenced but not the primary resource -- e.g. GET /devices/{id}/users, GET /apps/{id}/users, leaderboard responses. Never used as the response shape for user-primary endpoints.
User
Full user representation returned by list, detail, and update endpoints. Extends UserRef with embedded department assignments and audit timestamps.
departments is embedded here because it satisfies all three conditions from the embedding rule: it is bounded (1–3 per user), always needed for display, and static reference data. Devices and apps are unbounded and are exposed as separate sub-resource endpoints instead.
{
"id": "a1d97031-04e2-4907-a249-093f7436207b",
"username": "mikechang",
"user_key": "mikechang",
"status": "active",
"last_activity": {
"at": "2026-02-24T21:23:53.082Z",
"desktop_at": "2026-02-24T21:22:46.119Z",
"web_at": "2026-02-24T21:23:53.082Z"
},
"activities_past_24h_count": 42,
"departments": [
{
"id": "d1a2b3c4-e5f6-7890-abcd-ef1234567890",
"name": "Engineering",
"description": "Software development and infrastructure teams"
}
],
"created_at": "2026-02-05T21:29:34.214Z",
"updated_at": "2026-02-05T21:29:34.214Z"
}
| Field | Type | Nullable | Description |
|---|---|---|---|
All UserRef fields | id, username, user_key, status, last_activity -- see UserRef | ||
activities_past_24h_count | integer | No | Total activity count across all devices in the past 24 hours. 0 when no heartbeats exist. |
departments | DepartmentRef[] | No | Departments this user belongs to. Empty array if unassigned. |
created_at | string (ISO 8601) | No | When this user record was first created |
updated_at | string (ISO 8601) | No | When this user record was last modified |
DB source:
| DTO Field | DB Column |
|---|---|
| (UserRef fields) | see UserRef |
activities_past_24h_count | SUM(heartbeats.total_activity_count) WHERE user_id = ? AND generated_at_utc >= NOW() - INTERVAL '24 hours' |
departments | joined via user_departments + departments |
created_at | users.created_at |
updated_at | users.updated_at |
Omitted internal fields: client_id (in URL path), partner_id, created_by, updated_by
Used in: List Users, Get User, Update User, Bulk Update User Status, Bulk Archive Users, Bulk Activate Users, Bulk Delete Users
DepartmentRef
Minimal department reference embedded inside User.departments. Defined fully in the Departments domain.
{
"id": "d1a2b3c4-e5f6-7890-abcd-ef1234567890",
"name": "Engineering",
"description": "Software development and infrastructure teams"
}
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (uuid) | No | Stable unique identifier for the department |
name | string | No | Department display name |
description | string | Yes | Optional department description |
DB source: departments.id, departments.name, departments.description
BulkUserControlResult
Response shape used by bulk user status endpoints, including Bulk Update User Status, Archive, Activate, and Delete.
{
"succeeded": [
"a1d97031-04e2-4907-a249-093f7436207b",
"b2e08142-15f3-5018-b350-104g8547318c"
],
"failed": [
{
"id": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"error": "Cannot reactivate a deleted user"
}
]
}
| Field | Type | Description |
|---|---|---|
succeeded | string (uuid)[] | User IDs successfully updated |
failed | object[] | User IDs that failed with error details |
failed[].id | string (uuid) | The user ID that failed |
failed[].error | string | Reason for the failure (machine code or message, endpoint-specific) |