NICE CXone GET /agents/states returning empty array — agent not showing as logged in

Struggling to understand why the CXone REST API is returning an empty array for agent states when I know for a fact that agents are logged in and taking calls. I am building a lightweight Svelte status widget for our internal portal. The goal is to poll the agent status in real-time.

Context:

I have a SvelteKit server route (src/routes/api/agents/+server.ts) that uses the Fetch API to call the NICE CXone endpoint. I am using a valid OAuth access token generated via the Authorization Code Flow. The token works perfectly for other endpoints like /api/v2/interactions/search.

Here is the relevant code snippet from my server function:

export async function GET() {
 const token = 'Bearer <valid_access_token>';
 const response = await fetch('https://api.us.nice.incontact.com/ic3api/agents/states', {
 headers: {
 'Authorization': token,
 'Content-Type': 'application/json'
 }
 });

 const data = await response.json();
 return new Response(JSON.stringify(data), {
 headers: { 'content-type': 'application/json' }
 });
}

The HTTP status code is 200 OK. No errors. However, the JSON payload is always:

[]

I have verified the following steps:

  1. The agents are logged in via the CXone desktop client.
  2. The agents are assigned to skills and queues.
  3. The token has the agent scope.

I also tried adding query parameters like ?status=available and ?status=busy, but the result remains an empty array. Is there a specific header or query parameter required to return active sessions? Or is this endpoint deprecated in favor of the Conversations API?

Question:

Why is GET /ic3api/agents/states returning an empty array despite valid authentication and active agent sessions? How do I correctly query the current login state of agents using the CXone REST API in a Node.js environment?

Make sure you are hitting the correct endpoint for real-time presence. The /api/v2/agents endpoint returns configuration data, not live state. You need to use the Presence API specifically. If you are seeing an empty array, it is usually because the query parameters are missing the specific divisionId or the agent is not explicitly tagged with a presence entity that your OAuth token has scope to read.

Here is the correct approach using Python and the PureCloud SDK, which handles the token refresh and pagination automatically. This is far more reliable than raw fetch calls in SvelteKit for data integrity.

  1. Install the SDK: pip install genesys-cloud-purecloud-platform-client
  2. Use get_presence_users with explicit pagination.
from genesyscloud_platform_client.rest import ApiException
from platformClient import PlatformClient

# Initialize client (ensure you have the correct OAuth scopes: presence:read)
platform_client = PlatformClient()
presence_api = platform_client.presence_api

try:
 # Get first page of users with 'Available' status
 result = presence_api.get_presence_users(
 presence_entity_id="Available", 
 expand=["agentState"],
 page_size=25
 )
 
 if result.entities:
 for user in result.entities:
 print(f"User: {user.name}, State: {user.agent_state.name}")
 else:
 print("No agents found in this state.")
except ApiException as e:
 print(f"Exception when calling PresenceApi->get_presence_users: {e}\n")

Verify your OAuth token has the presence:read scope. If you are still getting empty results, check the Division filter. The API defaults to the root division if not specified, but your agents might be in a nested division. Add division_id to the request if needed. This avoids the CORS and token refresh headaches of client-side polling.

I think the presence endpoint requires the divisionid query param to filter results, otherwise it returns empty for multi-division tenants. try appending ?divisionid={your_division_id} to your request.

The official documentation states that the GET /api/v2/presence/users endpoint is strict about scope validation. While the previous suggestion regarding divisionId is technically correct for multi-division tenants, the empty array often stems from the OAuth token lacking the presence:read scope or the agents:read scope depending on your tenant configuration. In my Kotlin Android integration, I found that passing the divisionId without verifying the token’s effective permissions results in silent failures.

Ensure your service account or user token includes presence:read. If you are using the Genesys Cloud SDK, the PresenceApi client handles the base path, but you must explicitly pass the division ID in the request builder.

val presenceApi = PresenceApi(platformClient)
val result = presenceApi.getPresenceUsers(
 divisionId = "your-division-id",
 expand = listOf("agentState")
)

Without the expand parameter, you might get user objects but missing the actual state details, which could be misinterpreted as an empty result if your parser expects nested state objects.

Note: Verify the token scopes in the Genesys Cloud admin console under Applications → API Credentials.

It depends, but generally… the empty array issue is rarely about the endpoint itself but rather how the authentication context intersects with division filters in a multi-tenant setup. When building custom triggers or polling mechanisms in Zapier, I see this pattern often. The GET /api/v2/presence/users endpoint is strict. If your OAuth token has presence:read but your service account lacks visibility into the specific division where agents are logged in, the API returns [] instead of an error.

You must explicitly pass the divisionId query parameter. Without it, the API attempts to scope results to the token’s default division, which might be empty if your service account is not assigned to that division’s user group. Additionally, ensure your OAuth token includes both presence:read and agents:read scopes. The agents:read scope is often required to resolve the user IDs returned by the presence service.

Here is a corrected curl example demonstrating the required parameters:

curl -X GET "https://api.mypurecloud.com/api/v2/presence/users?divisionId=your_division_id" \
 -H "Authorization: Bearer {your_oauth_token}" \
 -H "Accept: application/json"

In my Zapier CLI custom trigger, I parse the response and map the presenceEntityId to the actual status name. If the array remains empty after adding the divisionId, verify that the agents are actually logged into a presence entity that is visible to your service account’s division. Sometimes, agents log into a global presence, but the API filters by division visibility. Check the division field in the agent configuration via GET /api/v2/users/{userId} to confirm alignment. This step is critical for reliable polling integrations.