Skip to main content

App Utilization Timeseries

Approved

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/timeseries

Returns a time-series of app counts classified into mutually exclusive buckets: unused, unapproved, AI, and other. Serves the stacked bar chart on the App Usage Dashboard.

Each data point represents one granularity unit (typically a month) and contains the count of distinct apps in each classification bucket.

For the paginated app metrics table, use App Utilization. For aggregate card counts, use App Utilization Summary.

Use Case

Use this endpoint to:

  • Render a stacked bar chart showing how app usage composition changes over time
  • Track growth or decline of unapproved or AI app usage
  • Identify months with spikes in unused apps (potential license waste)

Path Parameters

ParameterTypeDescription
client_idstring (uuid)Unique identifier for the client organization

Query Parameters

ParameterTypeRequiredDefaultDescription
granularitystringYes-Time bucket size: daily, weekly, monthly, quarterly. Determines the grouping of each data point.
start_datestringYes-Start of period (YYYY-MM-DD)
end_datestringYes-End of period, inclusive (YYYY-MM-DD)
department_idsstringNo-Comma-separated department UUIDs to scope usage data to

Granularity

granularity determines the time bucket for each data point. For a 12-month stacked bar chart, use monthly. Each bucket counts distinct apps classified in that period.

Response

FieldTypeDescription
periodPeriodResolved date range
dataTrendDataPoint[]One entry per granularity unit in the range

Example Requests

Monthly timeseries for the past year

curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/analytics/apps/utilization/timeseries?granularity=monthly&start_date=2025-06-01&end_date=2026-05-31" \
-H "Authorization: Bearer YOUR_API_TOKEN"

Scoped to a department

curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/analytics/apps/utilization/timeseries?granularity=monthly&start_date=2025-06-01&end_date=2026-05-31&department_ids=d1a2b3c4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer YOUR_API_TOKEN"

Example Response

{
"period": {
"start_date": "2025-06-01",
"end_date": "2026-05-31"
},
"data": [
{
"date": "2025-06-01",
"unused_apps": 18,
"unapproved_apps": 14,
"ai_apps": 3,
"other_apps": 55
},
{
"date": "2025-07-01",
"unused_apps": 10,
"unapproved_apps": 12,
"ai_apps": 4,
"other_apps": 62
},
{
"date": "2025-08-01",
"unused_apps": 8,
"unapproved_apps": 10,
"ai_apps": 5,
"other_apps": 68
},
{
"date": "2025-09-01",
"unused_apps": 7,
"unapproved_apps": 11,
"ai_apps": 5,
"other_apps": 70
},
{
"date": "2025-10-01",
"unused_apps": 12,
"unapproved_apps": 15,
"ai_apps": 5,
"other_apps": 65
},
{
"date": "2025-11-01",
"unused_apps": 6,
"unapproved_apps": 9,
"ai_apps": 6,
"other_apps": 72
}
]
}

Period

FieldTypeNullableDescription
start_datestringNoStart of the period (YYYY-MM-DD)
end_datestringNoEnd of the period (YYYY-MM-DD)

This endpoint does not support comparison periods. The time-series itself shows change over time.

TrendDataPoint

FieldTypeDescription
datestringStart date of the granularity bucket (YYYY-MM-DD). For monthly, this is the first day of the month.
unused_appsintegerApps known to the client with prior activity but zero activity in this bucket
unapproved_appsintegerApps with activity, is_approved=false, not AI-categorized
ai_appsintegerApps with activity that have an AI catalog category
other_appsintegerApps with activity that are approved and not AI-categorized

Classification Logic

Each app is assigned to exactly one bucket per time period. The classification is mutually exclusive and uses the following priority:

PriorityBucketCriteria
1unused_appsApp has prior usage with this client but zero rows in app_usage_reports for this time bucket
2ai_appsApp has activity AND has at least one AI catalog category (any approval status)
3unapproved_appsApp has activity AND is_approved = false AND not AI-categorized
4other_appsApp has activity AND is_approved = true AND not AI-categorized

An app that is both AI-categorized and unapproved is classified as ai_apps (the more specific classification wins).

The sum unused_apps + unapproved_apps + ai_apps + other_apps equals the total number of distinct apps known to the client for that time bucket.

DB source:

BucketDB Aggregation
unused_appsApps in apps table for this client with at least one historical app_usage_reports row but zero rows where activity_date falls within this bucket
ai_appsCOUNT(DISTINCT application_id) from app_usage_reports in this bucket, joined to AI category
unapproved_appsCOUNT(DISTINCT application_id) from app_usage_reports in this bucket, where apps.is_approved = false AND not AI-categorized
other_appsCOUNT(DISTINCT application_id) from app_usage_reports in this bucket, where apps.is_approved = true AND not AI-categorized

Constraints

  • Max date range: 366 days. Requests exceeding this return 400.
  • granularity is required. There is no default.
  • Zero-fill: Granularity buckets with no data return all counts as 0.
  • Timezone: All dates are in the client's configured timezone.
  • No comparison period. This endpoint shows change over time natively via the time-series itself.

Error Responses

StatusDescription
400Invalid parameters (bad date format, range exceeds 366 days, missing granularity)
401Authentication required
403Insufficient permissions for this client
404Client not found
500Server error

Example Error Response

{
"error": {
"code": "invalid_parameter",
"message": "granularity is required",
"details": null
}
}