Client Credentials vs Authorization Code for server-side reporting

Running into a scope mismatch on our nightly reporting job. We’ve been using the Authorization Code grant for an internal dashboard that needs to aggregate interaction data over the last 30 days. It works fine during the day when agents are logged in, but the token expires overnight and the refresh token logic gets messy in a headless cron job environment.

Switched to Client Credentials to keep things clean. No user context needed, just pure API access. Here’s the token request payload:

{
 "grant_type": "client_credentials",
 "client_id": "our-app-id",
 "client_secret": "super-secret-string"
}

Getting a 200 OK back from /oauth/token. The access token looks valid. But when I hit /api/v2/analytics/interactions/summary, I get a 403 Forbidden. The error message says “Access Denied”.

Checked the app permissions in Genesys Cloud. The API app has analytics:read and interaction:read enabled. Same scopes as the dashboard app that works with Auth Code.

Tried adding analytics:write just in case, no change.

Here’s the curl command for the API call:

curl -X GET "https://api.mypurecloud.com/api/v2/analytics/interactions/summary" \
-H "Authorization: Bearer <client_creds_token>" \
-H "Content-Type: application/json"

Response:

{
 "message": "Access Denied",
 "code": "forbidden",
 "status": 403
}

The Auth Code flow works perfectly for the same endpoint. The token from Client Credentials seems to be missing something.

Is there a specific permission or setting I’m missing for Client Credentials to access analytics? Or is this a known limitation where certain endpoints require a user-bound token?

Tried regenerating the client secret, no luck. Also checked the app’s assigned roles, but Client Credentials apps don’t seem to support role assignment in the same way.

Any ideas?