App Utilization by User
This endpoint is approved but has not been implemented yet. See the App Usage Dashboard Spec for full context.
GET /clients/{client_id}/analytics/apps/utilization/users
Returns a paginated list of users ranked by the number of distinct apps they used during the period. Each row contains a user reference, their department, and the app count with optional comparison-period change percentage.
This endpoint powers the "Users" tab in the AI Apps and Un-Approved Apps sections of the App Usage Dashboard. It is a dimension view — for per-app metrics, use App Utilization.
Use Case
Use this endpoint to:
- Render a users tab showing who uses the most apps
- Identify top users of unapproved apps with
is_approved=false - Identify top users of AI apps with
category_ids=<ai_id> - Compare user app adoption between time periods
Path Parameters
| Parameter | Type | Description |
|---|---|---|
client_id | string (uuid) | Unique identifier for the client organization |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
granularity | string | Yes | - | Aggregation granularity: daily, weekly, monthly, quarterly |
start_date | string | Yes | - | Start of period (YYYY-MM-DD) |
end_date | string | Yes | - | End of period, inclusive (YYYY-MM-DD) |
compare_start_date | string | No | - | Comparison period start (YYYY-MM-DD). Must be provided with compare_end_date. |
compare_end_date | string | No | - | Comparison period end, inclusive (YYYY-MM-DD). Must be provided with compare_start_date. |
is_approved | boolean | No | - | Filter to apps by approval status. When false, app_count reflects only unapproved apps each user interacted with. |
category_ids | string | No | - | Comma-separated catalog category UUIDs to filter apps by |
vendor_ids | string | No | - | Comma-separated catalog vendor UUIDs to filter apps by |
department_ids | string | No | - | Comma-separated department UUIDs to filter users by |
sort_by | string | No | app_count | Sort field: app_count, username |
sort_order | string | No | desc | Sort direction: asc, desc |
page_size | integer | No | 50 | Items per page. Max: 200 |
cursor | string | No | - | Pagination cursor from a previous response |
Comparison period
When both compare_start_date and compare_end_date are provided, app_count.change_pct contains the percentage change from the comparison period to the primary period. When omitted, app_count.change_pct is null.
Providing only one of the two comparison parameters returns 400.
Response
| Field | Type | Description |
|---|---|---|
period | Period | Resolved date range |
data | UserUtilization[] | Array of user utilization objects |
total_count | integer | Total number of users matching the current filters |
next_cursor | string | null | Cursor for the next page. null when on the last page. |
Example Requests
Top users of unapproved apps, February 2026
curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/analytics/apps/utilization/users?granularity=monthly&start_date=2026-02-01&end_date=2026-02-28&is_approved=false" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Top users of AI apps with comparison
curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/analytics/apps/utilization/users?granularity=monthly&start_date=2026-02-01&end_date=2026-02-28&compare_start_date=2026-01-01&compare_end_date=2026-01-31&category_ids=a7b8c9d0-e1f2-3456-abcd-789012345678" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Filtered by department
curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/analytics/apps/utilization/users?granularity=monthly&start_date=2026-02-01&end_date=2026-02-28&department_ids=d1a2b3c4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Example Responses
With comparison period (unapproved apps)
{
"period": {
"start_date": "2026-02-01",
"end_date": "2026-02-28",
"compare_start_date": "2026-01-01",
"compare_end_date": "2026-01-31"
},
"data": [
{
"user": {
"id": "u1a2b3c4-d5e6-7890-abcd-ef1234567890",
"username": "james.wilson",
"user_key": "james.wilson@example.com",
"last_activity": { "at": "2026-02-28T17:42:00.000Z", "desktop_at": "2026-02-28T17:42:00.000Z", "web_at": null }
},
"department": {
"id": "d1a2b3c4-e5f6-7890-abcd-ef1234567890",
"name": "Product",
"description": "Product management and design"
},
"app_count": { "value": 6, "change_pct": 50.0, "compare_value": 4 }
},
{
"user": {
"id": "u2b3c4d5-e6f7-8901-bcde-f12345678901",
"username": "sarah.chen",
"user_key": "sarah.chen@example.com",
"last_activity": { "at": "2026-02-28T16:10:00.000Z", "desktop_at": "2026-02-28T16:10:00.000Z", "web_at": "2026-02-27T09:00:00.000Z" }
},
"department": {
"id": "e2b3c4d5-f6a7-8901-bcde-f12345678901",
"name": "Engineering",
"description": "Software development and infrastructure teams"
},
"app_count": { "value": 5, "change_pct": 25.0, "compare_value": 4 }
},
{
"user": {
"id": "u3c4d5e6-f7a8-9012-cdef-123456789012",
"username": "alex.kim",
"user_key": "alex.kim@example.com",
"last_activity": { "at": "2026-02-25T11:30:00.000Z", "desktop_at": null, "web_at": "2026-02-25T11:30:00.000Z" }
},
"department": {
"id": "f3c4d5e6-a7b8-9012-cdef-123456789012",
"name": "Marketing",
"description": null
},
"app_count": { "value": 4, "change_pct": -20.0, "compare_value": 5 }
},
{
"user": {
"id": "u4d5e6f7-a8b9-0123-defa-234567890123",
"username": "maria.garcia",
"user_key": "maria.garcia@example.com",
"last_activity": { "at": "2026-02-24T14:55:00.000Z", "desktop_at": "2026-02-24T14:55:00.000Z", "web_at": null }
},
"department": {
"id": "a4d5e6f7-b8c9-0123-defa-234567890123",
"name": "Design",
"description": "Visual and product design"
},
"app_count": { "value": 3, "change_pct": null, "compare_value": null }
},
{
"user": {
"id": "u5e6f7a8-b9c0-1234-efab-345678901234",
"username": "david.park",
"user_key": "david.park@example.com",
"last_activity": { "at": "2026-02-28T08:20:00.000Z", "desktop_at": "2026-02-28T08:20:00.000Z", "web_at": "2026-02-27T15:00:00.000Z" }
},
"department": {
"id": "b5e6f7a8-c9d0-1234-efab-345678901234",
"name": "Sales",
"description": "Revenue and business development"
},
"app_count": { "value": 2, "change_pct": 0.0, "compare_value": 2 }
}
],
"total_count": 58,
"next_cursor": "eyJsYXN0X2lkIjoidTVlNmY3YTgifQ"
}
Without comparison period
{
"period": {
"start_date": "2026-02-01",
"end_date": "2026-02-28",
"compare_start_date": null,
"compare_end_date": null
},
"data": [
{
"user": {
"id": "u1a2b3c4-d5e6-7890-abcd-ef1234567890",
"username": "james.wilson",
"user_key": "james.wilson@example.com",
"last_activity": { "at": "2026-02-28T17:42:00.000Z", "desktop_at": "2026-02-28T17:42:00.000Z", "web_at": null }
},
"department": {
"id": "d1a2b3c4-e5f6-7890-abcd-ef1234567890",
"name": "Product",
"description": "Product management and design"
},
"app_count": { "value": 6, "change_pct": null, "compare_value": null }
}
],
"total_count": 58,
"next_cursor": "eyJsYXN0X2lkIjoidTFhMmIzYzQifQ"
}
Empty Result
{
"period": {
"start_date": "2026-02-01",
"end_date": "2026-02-28",
"compare_start_date": null,
"compare_end_date": null
},
"data": [],
"total_count": 0,
"next_cursor": null
}
Period
| Field | Type | Nullable | Description |
|---|---|---|---|
start_date | string | No | Start of the primary period (YYYY-MM-DD) |
end_date | string | No | End of the primary period (YYYY-MM-DD) |
compare_start_date | string | Yes | Start of comparison period. null if no comparison requested. |
compare_end_date | string | Yes | End of comparison period. null if no comparison requested. |
UserUtilization
| Field | Type | Nullable | Description |
|---|---|---|---|
user | UserRef | No | Lightweight user reference with identity fields |
department | DepartmentRef | Yes | The user's primary department. null if the user is not assigned to any department. |
app_count | MetricWithDeltaDto | No | Distinct apps this user interacted with during the period (scoped to any active filters) |
DB source:
| DTO Field | DB Aggregation |
|---|---|
user | Joined via app_usage_reports.user_id → users |
department | First department from user-department assignments |
app_count.value | COUNT(DISTINCT app_usage_reports.application_id) WHERE activity_date BETWEEN start_date AND end_date, grouped by user_id |
app_count.change_pct | ((current - comparison) / comparison) * 100 using the same aggregation over the comparison date range |
app_count.compare_value | The same aggregation as app_count.value applied to the comparison date range. null when no comparison period is requested. |
Pagination
This endpoint uses cursor-based pagination.
- Omit
cursoron the first request - If
next_cursoris notnull, pass its value ascursoron the next request - Repeat until
next_cursorisnull
Constraints
- Max date range: 366 days for both primary and comparison periods. Requests exceeding this return
400. - Comparison symmetry: Both
compare_start_dateandcompare_end_datemust be provided together, or both omitted. - Timezone: All dates are in the client's configured timezone.
granularityis required. There is no default.- Multi-department users: If a user belongs to multiple departments, the
departmentfield returns their first assigned department. The user appears once in the list, not once per department.
Error Responses
| Status | Description |
|---|---|
| 400 | Invalid parameters (bad date format, range exceeds 366 days, mismatched comparison params, missing granularity, unknown sort_by value) |
| 401 | Authentication required |
| 403 | Insufficient permissions for this client |
| 404 | Client not found |
| 500 | Server error |
Notes
- When
is_approved=falseis provided,app_countreflects only unapproved apps the user interacted with, not their total app count. - When
category_idsis provided,app_countreflects only apps in those categories. - Filters compose:
?is_approved=false&department_ids=<id>returns users in the specified department ranked by their unapproved app count.
Related Endpoints
- App Utilization -- Paginated per-app metrics table
- App Utilization Categories -- Categories dimension view
- App Utilization Departments -- Departments dimension view
- App Utilization Summary -- Aggregate card counts