403 Forbidden on /api/v2/routing/queues

It’s throwing a 403 forbidden error when hitting the queues endpoint. Token works fine for users but fails here. Script below. Scope config looks wrong.

curl -H "Authorization: Bearer $TOKEN" https://company.mypurecloud.com/api/v2/routing/queues

Check the scopes attached to that service account. The /routing/queues endpoint requires routing:queue:read (or routing:queue:admin if you are modifying things). If the token was generated for a user role that only has user:read, it will fail with a 403.

In the Admin UI, go to Security > Service Accounts. Find your account and check the API Scopes tab. Make sure routing:queue:read is checked. If you are using OAuth client credentials flow, verify the scope parameter in your token request matches what is allowed in the client settings.

# Example of what the scope should include
scope=routing:queue:read user:read

Also, double-check that the service account isn’t locked out due to too many failed attempts. It happens more often than you think. If the scopes look correct and it still fails, try regenerating the token. Sometimes the cached permissions don’t update immediately after a scope change.

403s on routing endpoints are rarely just about missing scopes if you’re hitting it from a service account with broad permissions. The real culprit is usually the role assignment. You can have routing:queue:read in the OAuth client config, but if the underlying service account user doesn’t have a role with View Queues permission, the API rejects it before it even checks the token scope.

Check the roles assigned to that service account in Admin > Security > Users. It needs a role like Routing Administrator or a custom role with the specific object permission. Also, verify the token isn’t expired or revoked. Here’s a quick check using the PureCloud SDK to validate the effective permissions:

const { PlatformClient } = require('@genesyscloud/genesyscloud');
const platformClient = PlatformClient.create();
platformClient.loginApplication('client_id', 'client_secret');

// Check if the token has the expected scope
platformClient.AuthApi.getOauthTokenInfo().then((result) => {
 console.log('Scopes:', result.body.scope);
}).catch((error) => console.error(error));

If the scope is there, the role is missing. Fix the role, rotate the token, and try again.

The previous point about roles is spot on. We see this constantly when spinning up new service accounts for IVR integrations. Having the routing:queue:read scope on the OAuth client is just the first step. If the user identity behind that token lacks the “View Queues” permission in their role, the API throws a 403 immediately. It’s a common pitfall in our agency setups where we grant broad API access but forget to align the underlying user permissions. Check the role matrix to ensure the specific permission is enabled.

For IVR flows that need to check queue status dynamically, we usually assign a custom role with minimal permissions. Just “View Queues” and “View Routing Data” is enough. This keeps the security footprint small while avoiding the 403 errors. If you are using a script to provision these accounts, make sure the role ID is hardcoded correctly. One wrong ID and the whole flow breaks. It’s better to verify the role permissions manually once before automating the rest.

The docs are pretty clear on the scope requirement, but people still miss the role check. **** is right about the OAuth scope, but **** nailed the real blocker. You can have every scope in the book, but if the service account user lacks the “View Queues” permission in their role, Genesys Cloud throws a 403 before it even looks at the token.

Here’s how I handle this in my SvelteKit server routes:

  1. Verify the OAuth client has routing:queue:read.
  2. Go to Admin > Security > Service Accounts.
  3. Check the roles assigned to that specific user.
  4. Ensure the role includes the View Queues permission.
// Quick check in your server function
const response = await fetch('/api/v2/routing/queues', {
 headers: { Authorization: `Bearer ${token}` }
});
if (response.status === 403) {
 console.error('Check role permissions, not just scopes');
}

It’s easily the most common mistake when spinning up new integrations. Don’t waste time regenerating tokens if the role is broken.