How to fetch all queues across multiple divisions using the Platform SDK for JavaScript

How to fetch all queues across multiple divisions using the Platform SDK for JavaScript

What You Will Build

  • You will build a Node.js script that retrieves every Queue resource configured in your Genesys Cloud organization, regardless of its division.
  • You will use the Genesys Cloud PureCloud Platform SDK for JavaScript (@genesyscloud/purecloud-platform-client-v2).
  • You will handle pagination, division iteration, and authentication token management programmatically.

Prerequisites

  • OAuth Client Type: Confidential Client (Client Credentials Grant).
  • Required Scopes: queue:view is mandatory. If you need to modify queues later, add queue:write.
  • SDK Version: @genesyscloud/purecloud-platform-client-v2 (latest stable).
  • Language/Runtime: Node.js 18+ (LTS recommended).
  • External Dependencies:
    • @genesyscloud/purecloud-platform-client-v2
    • dotenv (for secure credential management)

Authentication Setup

Genesys Cloud uses OAuth 2.0 for API authentication. For server-to-server integrations, the Client Credentials flow is the standard approach. The Platform SDK handles the token acquisition and refresh logic internally, provided you configure the PlatformClient correctly.

You must store your credentials securely. Never hardcode them in source code. Use environment variables.

  1. Create a .env file in your project root.
  2. Add your Client ID, Client Secret, and Region (Environment).
GENESYS_CLIENT_ID=your_client_id_here
GENESYS_CLIENT_SECRET=your_client_secret_here
GENESYS_ENVIRONMENT=us-east-1

The GENESYS_ENVIRONMENT variable determines the base URL for the API calls (e.g., us-east-1, us-east-2, eu-west-1).

SDK Initialization Code

Initialize the PlatformClient and set the authentication credentials. The SDK will automatically request an access token when the first API call is made.

const { PlatformClient } = require('@genesyscloud/purecloud-platform-client-v2');
require('dotenv').config();

// Initialize the platform client
const platformClient = new PlatformClient();

// Configure authentication
const auth = platformClient.auth;

// Set the environment (region)
auth.setEnvironment(process.env.GENESYS_ENVIRONMENT);

// Set the client credentials
auth.setClientCredentials(
    process.env.GENESYS_CLIENT_ID,
    process.env.GENESYS_CLIENT_SECRET
);

console.log('Auth configured. Environment:', process.env.GENESYS_ENVIRONMENT);

Implementation

Step 1: Retrieve All Divisions

Queues in Genesys Cloud are scoped to Divisions. To fetch all queues, you must first identify all divisions in the organization, then query queues within each division. The DivisionsApi provides a list of all divisions.

Endpoint: GET /api/v2/organizations/divisions
SDK Method: divisionsApi.getDivisions()
Scope: division:view

The response is paginated. The SDK provides helper methods to simplify pagination, but for this tutorial, we will use the explicit pagination loop to demonstrate control over the request cycle.

async function getAllDivisions(platformClient) {
    const divisionsApi = platformClient.DivisionsApi();
    const allDivisions = [];
    
    let cursor = null;
    let hasNextPage = true;

    console.log('Fetching divisions...');

    while (hasNextPage) {
        try {
            // Get up to 100 divisions per page
            const response = await divisionsApi.getDivisions({
                pageSize: 100,
                cursor: cursor
            });

            if (response.body && response.body.entities) {
                allDivisions.push(...response.body.entities);
            }

            // Check if there are more pages
            if (response.body && response.body.nextPageCursor) {
                cursor = response.body.nextPageCursor;
            } else {
                hasNextPage = false;
            }
        } catch (error) {
            console.error('Error fetching divisions:', error);
            throw error;
        }
    }

    console.log(`Retrieved ${allDivisions.length} divisions.`);
    return allDivisions;
}

Step 2: Fetch Queues for Each Division

The QueuesApi allows you to list queues. Crucially, the divisionId parameter is required. If you omit it, the API returns queues only from the default division. To get all queues, you must iterate through the list of divisions obtained in Step 1.

Endpoint: GET /api/v2/queues
SDK Method: queuesApi.getQueues({ divisionId })
Scope: queue:view

Important: Handling Division-Specific Queues

Some queues may exist in the default division (often default or system). Ensure your division list includes the default division. The getDivisions endpoint returns all divisions, including the default one, so no special handling is needed beyond iterating the full list.

async function getQueuesForDivision(platformClient, divisionId) {
    const queuesApi = platformClient.QueuesApi();
    const allQueues = [];
    
    let cursor = null;
    let hasNextPage = true;

    console.log(`Fetching queues for division: ${divisionId}`);

    while (hasNextPage) {
        try {
            // Fetch queues for the specific division
            const response = await queuesApi.getQueues({
                divisionId: divisionId,
                pageSize: 100,
                cursor: cursor
            });

            if (response.body && response.body.entities) {
                allQueues.push(...response.body.entities);
            }

            // Check if there are more pages
            if (response.body && response.body.nextPageCursor) {
                cursor = response.body.nextPageCursor;
            } else {
                hasNextPage = false;
            }
        } catch (error) {
            // Handle specific HTTP errors
            if (error.status === 403) {
                console.warn(`Permission denied for division ${divisionId}. Skipping.`);
                return [];
            } else if (error.status === 429) {
                console.warn(`Rate limited. Waiting 1 second before retrying...`);
                await new Promise(resolve => setTimeout(resolve, 1000));
                // Retry logic should ideally be more robust, but this suffices for simple scripts
                continue; 
            } else {
                console.error(`Error fetching queues for division ${divisionId}:`, error);
                throw error;
            }
        }
    }

    console.log(`Retrieved ${allQueues.length} queues for division ${divisionId}.`);
    return allQueues;
}

Step 3: Aggregate and Process Results

Combine the queues from all divisions into a single array. You may want to enrich the data with division names for easier identification.

async function fetchAllQueues(platformClient) {
    try {
        // Step 1: Get all divisions
        const divisions = await getAllDivisions(platformClient);
        
        if (divisions.length === 0) {
            console.log('No divisions found.');
            return [];
        }

        // Step 2: Fetch queues for each division
        const allQueues = [];
        
        for (const division of divisions) {
            const divisionQueues = await getQueuesForDivision(platformClient, division.id);
            allQueues.push(...divisionQueues);
            
            // Optional: Add a small delay between division requests to avoid rate limiting
            // if the organization has a very large number of divisions.
            if (divisions.indexOf(division) < divisions.length - 1) {
                await new Promise(resolve => setTimeout(resolve, 50));
            }
        }

        console.log(`Total queues retrieved: ${allQueues.length}`);
        return allQueues;

    } catch (error) {
        console.error('Failed to fetch all queues:', error);
        throw error;
    }
}

Complete Working Example

This is a complete, runnable Node.js script. Save it as fetch-all-queues.js.

const { PlatformClient } = require('@genesyscloud/purecloud-platform-client-v2');
require('dotenv').config();

/**
 * Fetches all divisions from Genesys Cloud.
 * @param {PlatformClient} platformClient
 * @returns {Promise<Array>} List of division objects.
 */
async function getAllDivisions(platformClient) {
    const divisionsApi = platformClient.DivisionsApi();
    const allDivisions = [];
    
    let cursor = null;
    let hasNextPage = true;

    console.log('Fetching divisions...');

    while (hasNextPage) {
        try {
            const response = await divisionsApi.getDivisions({
                pageSize: 100,
                cursor: cursor
            });

            if (response.body && response.body.entities) {
                allDivisions.push(...response.body.entities);
            }

            if (response.body && response.body.nextPageCursor) {
                cursor = response.body.nextPageCursor;
            } else {
                hasNextPage = false;
            }
        } catch (error) {
            console.error('Error fetching divisions:', error);
            throw error;
        }
    }

    console.log(`Retrieved ${allDivisions.length} divisions.`);
    return allDivisions;
}

/**
 * Fetches all queues for a specific division.
 * @param {PlatformClient} platformClient
 * @param {string} divisionId
 * @returns {Promise<Array>} List of queue objects.
 */
async function getQueuesForDivision(platformClient, divisionId) {
    const queuesApi = platformClient.QueuesApi();
    const allQueues = [];
    
    let cursor = null;
    let hasNextPage = true;

    console.log(`Fetching queues for division: ${divisionId}`);

    while (hasNextPage) {
        try {
            const response = await queuesApi.getQueues({
                divisionId: divisionId,
                pageSize: 100,
                cursor: cursor
            });

            if (response.body && response.body.entities) {
                allQueues.push(...response.body.entities);
            }

            if (response.body && response.body.nextPageCursor) {
                cursor = response.body.nextPageCursor;
            } else {
                hasNextPage = false;
            }
        } catch (error) {
            if (error.status === 403) {
                console.warn(`Permission denied for division ${divisionId}. Skipping.`);
                return [];
            } else if (error.status === 429) {
                console.warn(`Rate limited. Waiting 1 second before retrying...`);
                await new Promise(resolve => setTimeout(resolve, 1000));
                continue; 
            } else {
                console.error(`Error fetching queues for division ${divisionId}:`, error);
                throw error;
            }
        }
    }

    console.log(`Retrieved ${allQueues.length} queues for division ${divisionId}.`);
    return allQueues;
}

/**
 * Main function to orchestrate the fetching of all queues.
 */
async function main() {
    // Validate environment variables
    if (!process.env.GENESYS_CLIENT_ID || !process.env.GENESYS_CLIENT_SECRET || !process.env.GENESYS_ENVIRONMENT) {
        console.error('Missing required environment variables: GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, GENESYS_ENVIRONMENT');
        process.exit(1);
    }

    // Initialize platform client
    const platformClient = new PlatformClient();
    const auth = platformClient.auth;
    auth.setEnvironment(process.env.GENESYS_ENVIRONMENT);
    auth.setClientCredentials(process.env.GENESYS_CLIENT_ID, process.env.GENESYS_CLIENT_SECRET);

    try {
        const allQueues = await fetchAllQueues(platformClient);
        
        // Example: Print a summary of queues
        console.log('\n--- Queue Summary ---');
        allQueues.forEach(queue => {
            console.log(`ID: ${queue.id}, Name: ${queue.name}, Division: ${queue.division.id}`);
        });
        
        // Example: Save to JSON file
        const fs = require('fs');
        fs.writeFileSync('all_queues.json', JSON.stringify(allQueues, null, 2));
        console.log('\nQueues saved to all_queues.json');

    } catch (error) {
        console.error('Application failed:', error);
        process.exit(1);
    }
}

// Helper function to combine steps
async function fetchAllQueues(platformClient) {
    try {
        const divisions = await getAllDivisions(platformClient);
        
        if (divisions.length === 0) {
            console.log('No divisions found.');
            return [];
        }

        const allQueues = [];
        
        for (const division of divisions) {
            const divisionQueues = await getQueuesForDivision(platformClient, division.id);
            allQueues.push(...divisionQueues);
            
            // Small delay to prevent rate limiting on large orgs
            if (divisions.indexOf(division) < divisions.length - 1) {
                await new Promise(resolve => setTimeout(resolve, 50));
            }
        }

        console.log(`Total queues retrieved: ${allQueues.length}`);
        return allQueues;

    } catch (error) {
        console.error('Failed to fetch all queues:', error);
        throw error;
    }
}

// Run the main function
main();

Common Errors & Debugging

Error: 401 Unauthorized

  • Cause: The OAuth token is invalid, expired, or the Client ID/Secret is incorrect.
  • Fix: Verify your .env file contains the correct Client ID and Secret. Ensure the client credentials are configured in the Genesys Cloud Admin Console under Security > OAuth 2.0 Clients. Check that the client is not disabled.

Error: 403 Forbidden

  • Cause: The OAuth client lacks the required scope (queue:view).
  • Fix: In the Genesys Cloud Admin Console, edit your OAuth client. Add queue:view to the list of scopes. Save the changes. Note: Existing tokens may need to be refreshed or regenerated.

Error: 429 Too Many Requests

  • Cause: You have exceeded the API rate limit. Genesys Cloud enforces rate limits per client and per tenant.
  • Fix: Implement exponential backoff. The code example above includes a simple 1-second delay on 429 errors. For production systems, implement a proper retry mechanism with exponential backoff (e.g., wait 1s, 2s, 4s, 8s).

Error: TypeError: Cannot read properties of undefined (reading ‘entities’)

  • Cause: The API response body is not structured as expected, often due to an unexpected error response or network issue.
  • Fix: Ensure you check for response.body before accessing entities. The code example includes these checks.

Official References