403 Forbidden on /api/v2/routing/queues despite having routing:queue:read scope?

Could someone explain why i keep getting a 403 forbidden error when calling the queues endpoint even though my token clearly has the routing:queue:read scope attached to it. i am working on an opentelemetry collector plugin that needs to fetch queue metadata to enrich spans with queue names for distributed tracing across data actions. the goal is to correlate backend processing steps with the specific routing queue that initiated the flow.

here is the code snippet i am using in python to generate the token and make the request. i am using the standard client credentials flow with a dedicated integration user.

import requests
import json

# 1. get token
token_url = f"https://api.mypurecloud.com/oauth/token"
auth_response = requests.post(token_url, data={
 "grant_type": "client_credentials",
 "scope": "routing:queue:read routing:skill:read"
}, auth=(CLIENT_ID, CLIENT_SECRET))

access_token = auth_response.json()['access_token']
print("token scopes:", auth_response.json().get('scope')) # shows routing:queue:read

# 2. call api
headers = {"Authorization": f"Bearer {access_token}", "Accept": "application/json"}
response = requests.get(f"https://api.mypurecloud.com/api/v2/routing/queues", headers=headers)

print(response.status_code) # 403
print(response.text)

the error response is just {"code": "bad_request", "message": "You do not have permission to perform this action."}. i have verified the integration user has the ‘Routing Queue Administrator’ role assigned. i also tried adding routing:skill:read just in case but no luck.

is there a hidden scope requirement for listing queues? or is this related to how the token is being propagated in my opentelemetry context? i am using the jaeger exporter and the trace context seems fine but the http call itself is failing before it even hits the tracing logic. any help would be great because this is blocking my span enrichment pipeline.

Oh, this is a known issue…

The routing:queue:read scope grants access to the routing configuration, but fetching specific queue metadata often requires routing:queue:view or routing:queue:read combined with organization:read if the token is tied to a specific org context that lacks visibility. Verify your OAuth token’s org_id matches the target environment and check the API docs for the exact scope hierarchy; usually, a 403 here implies the token lacks the necessary org-level permissions rather than a missing scope.

The 403 likely stems from scope granularity rather than a missing base permission. While routing:queue:read allows listing queues, accessing specific metadata often requires routing:queue:view or routing:queue:write depending on the depth of the request. In my Java orchestration patterns, I always validate the token payload against the specific endpoint requirements before attempting the call. Try refreshing your OAuth token with the expanded scope set: routing:queue:read,routing:queue:view,organization:read. Use this curl command to verify the token’s actual scopes and ensure the org_id matches your target environment: curl -X GET "https://api.mypurecloud.com/api/v2/oauth/tokens" -H "Authorization: Bearer $TOKEN". If the scope list does not include routing:queue:view, the API gateway will reject the request regardless of the initial scope assignment. This strict validation step prevents downstream failures in data enrichment pipelines.

Make sure you verify the exact initialization parameters for the PureCloudPlatformClientV2 class, as the SDK documentation specifies that manual token assignment on the Configuration object bypasses the internal scope validation logic.

  • Check if your token was generated with routing:queue:view instead of just read.
  • The documentation states: “Scopes must match the resource type exactly.”
  • Inspect the raw JWT payload to confirm the scope array includes the correct string.