Fetching All Queues Across Multiple Divisions Using the Genesys Cloud Platform SDK for JavaScript

Fetching All Queues Across Multiple Divisions Using the Genesys Cloud Platform SDK for JavaScript

What You Will Build

This tutorial provides a production-ready JavaScript module that retrieves every queue in a Genesys Cloud organization, regardless of division isolation settings. The code utilizes the Genesys Cloud Platform SDK for JavaScript to handle OAuth2 authentication, iterative pagination, and multi-division querying. The final output is a consolidated array of queue objects containing metadata such as name, ID, description, and associated division.

Prerequisites

To execute this code, you must have the following environment and credentials configured:

  • Genesys Cloud OAuth Client: A confidential client type (server-to-server) is recommended for backend scripts.
  • Required OAuth Scopes: The client must have the view:queue scope. If you need to update queues later, add manage:queue.
  • Node.js Runtime: Version 18 or higher is recommended for native fetch support and modern async/await behavior.
  • Genesys Cloud Platform SDK for JavaScript: Version 150 or higher.
  • Environment Variables: You must store your GENESYS_CLOUD_REGION, GENESYS_CLOUD_CLIENT_ID, and GENESYS_CLOUD_CLIENT_SECRET in a .env file or your system environment.

Install the necessary dependencies using npm:

npm install @genesys/cloud-purecloud-sdk dotenv

Authentication Setup

The Genesys Cloud Platform SDK for JavaScript includes a built-in PureCloudAuth module that handles the OAuth2 client credentials flow. This module manages token acquisition, caching, and automatic refresh when the access token expires.

In a production environment, you should initialize the auth object once and share it across your application lifecycle to avoid unnecessary network requests for token renewal.

import 'dotenv/config';
import { PureCloudAuth } from '@genesys/cloud-purecloud-sdk';

const auth = new PureCloudAuth({
  clientId: process.env.GENESYS_CLOUD_CLIENT_ID,
  clientSecret: process.env.GENESYS_CLOUD_CLIENT_SECRET,
  region: process.env.GENESYS_CLOUD_REGION || 'us-east-1'
});

// Initialize the auth object. This fetches the initial token.
// In an async context, you would await this.
auth.initialize().catch(err => {
  console.error("Failed to initialize authentication:", err);
  process.exit(1);
});

The region parameter determines the base URL for all API calls. Common values include us-east-1, us-east-2, eu-west-1, ap-southeast-2, etc. If your client was created in a specific region, you must match that region here.

Implementation

Step 1: Initialize the Platform Client and Queue API

The SDK exposes domain-specific API clients. For queue operations, we use the QueueApi. This client inherits the authentication context from the auth object initialized in the previous step.

import { PlatformClient } from '@genesys/cloud-purecloud-sdk';

// Initialize the platform client with the auth object
const platformClient = new PlatformClient();
platformClient.setAuth(auth);

// Initialize the Queue API client
const queueApi = platformClient.queueApi;

Step 2: Retrieve All Divisions

Genesys Cloud uses divisions to isolate data. A user or API client may have access to multiple divisions. To fetch “all” queues, we must first identify which divisions the client can access.

The endpoint /api/v2/users/me/divisions returns the divisions accessible to the authenticated identity. However, a more robust approach for server-to-server integrations is to use /api/v2/divisions with the expand parameter to get a list of all divisions in the organization that the client has visibility into.

async function getAllDivisions() {
  const divisions = [];
  
  try {
    // Fetch divisions. The expand parameter ensures we get full details.
    // We use the raw API call pattern for clarity, though SDK methods exist.
    const response = await queueApi.getDivisions({
      expand: ['name', 'id']
    });

    // The response body contains an array of division objects
    if (response.body && Array.isArray(response.body)) {
      return response.body;
    } else {
      console.warn("No divisions returned or unexpected response format.");
      return [];
    }
  } catch (error) {
    if (error.status === 401 || error.status === 403) {
      console.error("Authentication or Authorization error fetching divisions. Check scopes and client permissions.");
    } else {
      console.error("Error fetching divisions:", error);
    }
    throw error;
  }
}

Step 3: Fetch Queues Per Division with Pagination

The endpoint /api/v2/queues supports pagination via the pageSize and pageNumber parameters. It also supports filtering by division using the divisionId parameter.

To ensure we capture every queue, we must implement a loop that continues fetching pages until no more results are returned. We will iterate through each division obtained in Step 2.

async function fetchQueuesForDivision(divisionId) {
  const allQueues = [];
  let pageNumber = 1;
  const pageSize = 250; // Max allowed page size for most Genesys Cloud list endpoints
  
  let hasMoreResults = true;

  while (hasMoreResults) {
    try {
      // Call the Get Queues API
      const response = await queueApi.getQueues({
        divisionId: divisionId,
        pageNumber: pageNumber,
        pageSize: pageSize,
        expand: ['routingEmail', 'routingChat', 'routingSocialMessaging'] // Optional: expand related configs
      });

      const queues = response.body;

      // If the response is empty, we have fetched all queues for this division
      if (!queues || queues.length === 0) {
        hasMoreResults = false;
      } else {
        // Append the current page of queues to our accumulator
        allQueues.push(...queues);
        
        // Check if there are more pages
        // The API does not explicitly return a 'hasMore' flag in the body for this endpoint,
        // so we rely on the fact that if the returned array length is less than pageSize,
        // it is likely the last page. However, to be safe, we can check if the length equals pageSize.
        if (queues.length < pageSize) {
          hasMoreResults = false;
        } else {
          pageNumber++;
        }
      }
    } catch (error) {
      // Handle specific HTTP errors
      if (error.status === 429) {
        console.warn("Rate limit hit. Retrying in 1 second...");
        await new Promise(resolve => setTimeout(resolve, 1000));
        continue; // Retry the same page
      } else if (error.status === 404) {
        console.log(`No queues found for division ID: ${divisionId}`);
        hasMoreResults = false;
      } else {
        console.error(`Error fetching queues for division ${divisionId}:`, error);
        throw error;
      }
    }
  }

  return allQueues;
}

Step 4: Consolidate Results

Now we combine the division retrieval and queue fetching logic into a single orchestrator function. This function iterates over each division and aggregates the queues.

async function getAllQueuesAcrossDivisions() {
  const allQueuesMap = new Map(); // Use a Map to prevent duplicates if a queue appears in multiple contexts (rare but possible in complex hierarchies)
  
  // 1. Get all accessible divisions
  const divisions = await getAllDivisions();
  
  console.log(`Found ${divisions.length} accessible divisions.`);

  // 2. Fetch queues for each division
  for (const division of divisions) {
    const divisionId = division.id;
    const divisionName = division.name || 'Unknown Division';
    
    console.log(`Fetching queues for division: ${divisionName} (${divisionId})`);
    
    try {
      const queues = await fetchQueuesForDivision(divisionId);
      
      // Add queues to the map using queue ID as key to ensure uniqueness
      for (const queue of queues) {
        // Attach division name to the queue object for easier downstream processing
        const queueWithDivision = {
          ...queue,
          _divisionName: divisionName,
          _divisionId: divisionId
        };
        allQueuesMap.set(queue.id, queueWithDivision);
      }
      
      console.log(`  - Retrieved ${queues.length} queues.`);
    } catch (error) {
      console.error(`  - Failed to retrieve queues for division ${divisionName}:`, error.message);
    }
  }

  // Convert Map values to an array
  const consolidatedQueues = Array.from(allQueuesMap.values());
  console.log(`Total unique queues retrieved: ${consolidatedQueues.length}`);
  
  return consolidatedQueues;
}

Complete Working Example

The following is a complete, runnable Node.js script. Save this as fetch-all-queues.js.

import 'dotenv/config';
import { PlatformClient, PureCloudAuth } from '@genesys/cloud-purecloud-sdk';

/**
 * Initializes the Genesys Cloud Platform Client and Auth.
 * @returns {PlatformClient} The configured platform client.
 */
async function initializeClient() {
  const auth = new PureCloudAuth({
    clientId: process.env.GENESYS_CLOUD_CLIENT_ID,
    clientSecret: process.env.GENESYS_CLOUD_CLIENT_SECRET,
    region: process.env.GENESYS_CLOUD_REGION || 'us-east-1'
  });

  try {
    await auth.initialize();
  } catch (error) {
    console.error("Failed to initialize authentication. Check your environment variables.");
    throw error;
  }

  const platformClient = new PlatformClient();
  platformClient.setAuth(auth);
  return platformClient;
}

/**
 * Fetches all divisions accessible to the current client.
 * @param {PlatformClient} client 
 * @returns {Array} List of division objects.
 */
async function getAccessibleDivisions(client) {
  const queueApi = client.queueApi;
  
  try {
    // GET /api/v2/divisions
    const response = await queueApi.getDivisions({
      expand: ['name', 'id', 'description']
    });

    if (!response.body || !Array.isArray(response.body)) {
      console.warn("Unexpected response format from divisions API.");
      return [];
    }
    return response.body;
  } catch (error) {
    console.error("Error fetching divisions:", error);
    throw error;
  }
}

/**
 * Fetches all queues for a specific division with pagination handling.
 * @param {any} queueApi 
 * @param {string} divisionId 
 * @returns {Array} List of queue objects.
 */
async function fetchQueuesForDivision(queueApi, divisionId) {
  const queues = [];
  let pageNumber = 1;
  const pageSize = 250;
  let hasMore = true;

  while (hasMore) {
    try {
      // GET /api/v2/queues
      const response = await queueApi.getQueues({
        divisionId: divisionId,
        pageNumber: pageNumber,
        pageSize: pageSize
      });

      const pageQueues = response.body;

      if (!pageQueues || pageQueues.length === 0) {
        hasMore = false;
      } else {
        queues.push(...pageQueues);
        
        // If the number of items returned is less than the page size, 
        // we have reached the end of the results.
        if (pageQueues.length < pageSize) {
          hasMore = false;
        } else {
          pageNumber++;
        }
      }
    } catch (error) {
      if (error.status === 429) {
        console.warn("Rate limited (429). Waiting 1s before retry...");
        await new Promise(r => setTimeout(r, 1000));
        continue;
      }
      throw error;
    }
  }
  return queues;
}

/**
 * Main execution function.
 */
async function main() {
  try {
    console.log("Initializing Genesys Cloud Client...");
    const client = await initializeClient();
    const queueApi = client.queueApi;

    console.log("Fetching accessible divisions...");
    const divisions = await getAccessibleDivisions(client);
    
    if (divisions.length === 0) {
      console.log("No divisions found. Exiting.");
      process.exit(0);
    }

    console.log(`Found ${divisions.length} divisions.`);
    const allQueues = [];

    for (const division of divisions) {
      console.log(`Processing division: ${division.name || 'Unnamed'} (ID: ${division.id})`);
      
      const divisionQueues = await fetchQueuesForDivision(queueApi, division.id);
      
      // Enrich queue objects with division context
      divisionQueues.forEach(queue => {
        queue._divisionName = division.name;
        queue._divisionId = division.id;
      });
      
      allQueues.push(...divisionQueues);
      console.log(`  - Added ${divisionQueues.length} queues.`);
    }

    console.log(`\nSuccess! Total queues fetched: ${allQueues.length}`);
    
    // Example: Output first 5 queues to console
    if (allQueues.length > 0) {
      console.log("\nSample Queue Data (First 5):");
      console.log(JSON.stringify(allQueues.slice(0, 5), null, 2));
    }

  } catch (error) {
    console.error("Fatal error during execution:", error);
    process.exit(1);
  }
}

// Run the script
main();

Common Errors & Debugging

Error: 401 Unauthorized

Cause: The OAuth token is invalid, expired, or the client credentials are incorrect.

Fix: Ensure GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET are correct in your .env file. Verify that the client is active in the Genesys Cloud Admin Portal. The PureCloudAuth module handles refresh tokens automatically, but if the initial grant fails, it will throw a 401.

// Check environment variables before initializing
if (!process.env.GENESYS_CLOUD_CLIENT_ID || !process.env.GENESYS_CLOUD_CLIENT_SECRET) {
  throw new Error("Missing required environment variables: GENESYS_CLOUD_CLIENT_ID, GENESYS_CLOUD_CLIENT_SECRET");
}

Error: 403 Forbidden

Cause: The OAuth client does not have the required scopes, or the user associated with the client does not have permission to view queues in the specific division.

Fix:

  1. Check the OAuth Client scopes in the Genesys Cloud Admin Portal. Ensure view:queue is selected.
  2. If using a user-based token, ensure the user has the “View Queue” permission in the relevant division.
  3. If using a confidential client, ensure the client is assigned to a role that has queue viewing permissions.

Error: 429 Too Many Requests

Cause: You have exceeded the rate limits for the Genesys Cloud API. The Queue API has specific rate limits per tenant.

Fix: Implement exponential backoff or a simple delay. The code above includes a basic 1-second retry loop for 429 errors. For higher volume integrations, implement a proper queueing mechanism or reduce the frequency of calls.

// Enhanced retry logic example
async function retryOn429(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status === 429) {
        const delay = Math.pow(2, i) * 1000; // Exponential backoff
        console.warn(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw error;
      }
    }
  }
  throw new Error("Max retries exceeded due to rate limiting.");
}

Error: Missing Queues in Certain Divisions

Cause: The API client does not have visibility into all divisions. Genesys Cloud enforces division isolation. If a division is created but the client/user is not added to it, the API will not return queues from that division.

Fix: Verify division assignments in the Genesys Cloud Admin Portal. For confidential clients, ensure the client is added to the “Divisions” list in the client configuration.

Official References