Fetching All Queues Across Multiple Divisions with the Genesys Cloud Platform SDK for JavaScript
What You Will Build
- A Node.js script that authenticates with the Genesys Cloud API and retrieves a comprehensive list of all queues across every division in an organization.
- This tutorial utilizes the
genesys-cloudNPM package (Platform SDK for JavaScript) to handle OAuth flows and API pagination. - The implementation is written in modern JavaScript using
async/awaitand handles multi-division pagination logic explicitly.
Prerequisites
- OAuth Client Type: A Genesys Cloud OAuth Client with the
publicorconfidentialgrant type. For local scripts,publicis sufficient. - Required Scopes: The OAuth client must have the
analytics:query:readorrouting:queue:readscope.routing:queue:readis the standard scope for accessing queue definitions. - SDK Version:
genesys-cloudversion 2.x or later. - Language/Runtime: Node.js version 16 or higher.
- External Dependencies:
genesys-cloud: The official Genesys Cloud SDK.dotenv: For managing environment variables securely.
Install the dependencies using npm:
npm install genesys-cloud dotenv
Create a .env file in your project root with the following variables:
GENESYS_CLOUD_REGION=us-east-1
GENESYS_CLOUD_CLIENT_ID=your_client_id_here
GENESYS_CLOUD_CLIENT_SECRET=your_client_secret_here
Authentication Setup
The Genesys Cloud SDK handles the OAuth 2.0 Client Credentials flow automatically when you instantiate the PlatformClient. You do not need to manually construct HTTP requests for token acquisition. The SDK caches the token and refreshes it automatically before expiration.
Initialize the SDK and configure the environment region. The region determines the base URL for all API calls.
const { PlatformClient } = require('genesys-cloud');
require('dotenv').config();
// Initialize the platform client
const platformClient = new PlatformClient();
// Configure the environment
// Use the region from environment variables, defaulting to us-east-1
const region = process.env.GENESYS_CLOUD_REGION || 'us-east-1';
platformClient.setEnvironment(region);
// Set the OAuth client credentials
// The SDK will automatically fetch and manage the access token
platformClient.setAuthClientCredentials(
process.env.GENESYS_CLOUD_CLIENT_ID,
process.env.GENESYS_CLOUD_CLIENT_SECRET
);
OAuth Scope Verification: If the provided client ID lacks the routing:queue:read scope, the SDK will throw a 403 Forbidden error during the first API call. Ensure your client permissions are configured correctly in the Genesys Cloud Admin UI under Settings > Security > OAuth Client.
Implementation
Step 1: Understanding Division and Pagination Logic
The Genesys Cloud API is multi-tenant and multi-division. A single organization can have multiple divisions (e.g., North America, Europe, Asia). The /api/v2/routing/queues endpoint supports fetching queues for a specific division. To fetch all queues, you must:
- Identify all active divisions in the organization.
- Iterate through each division ID.
- Fetch queues for that division, handling pagination within each division.
The SDK provides a paging helper for endpoints that support cursors. However, for the Queues API, it is often more reliable to use the pageSize and pageNumber parameters or the divisionId filter directly. We will use the routingApi.getRoutingQueues method.
First, retrieve the list of divisions. The Divisions API (/api/v2/organizations/divisions) returns all divisions accessible to the authenticated user.
async function getAllDivisions() {
try {
// The divisionsApi is part of the platformClient
const divisionsApi = platformClient.DivisionsApi;
// Fetch all divisions with a large page size to minimize requests
// Note: The divisions endpoint supports paging via 'pageSize' and 'pageNumber'
const response = await divisionsApi.getOrganizationsDivisions({
pageSize: 100,
pageNumber: 1
});
// The response body contains a 'entities' array
if (!response.body || !response.body.entities) {
console.error('No divisions found or invalid response structure.');
return [];
}
return response.body.entities;
} catch (error) {
console.error('Error fetching divisions:', error.message);
throw error;
}
}
Step 2: Fetching Queues for a Single Division
Now, implement a function to fetch queues for a specific division ID. This function must handle pagination because a single division may have more queues than the default page size (usually 25). We will use a loop to fetch all pages until no more queues are returned.
async function getQueuesForDivision(divisionId) {
const routingApi = platformClient.RoutingApi;
const allQueues = [];
let pageNumber = 1;
const pageSize = 100; // Max allowed page size for queues is usually 100
let hasMorePages = true;
while (hasMorePages) {
try {
const response = await routingApi.getRoutingQueues({
divisionId: divisionId,
pageSize: pageSize,
pageNumber: pageNumber
});
const queues = response.body.entities;
if (!queues || queues.length === 0) {
hasMorePages = false;
break;
}
// Add queues from this page to the accumulator
allQueues.push(...queues);
// Check if there are more pages
// The API does not explicitly return a 'hasMore' flag in the same way some do,
// so we check if the returned count equals the requested page size.
if (queues.length < pageSize) {
hasMorePages = false;
} else {
pageNumber++;
}
} catch (error) {
console.error(`Error fetching queues for division ${divisionId}:`, error.message);
throw error;
}
}
return allQueues;
}
Step 3: Aggregating Results Across All Divisions
Combine the division fetching and queue fetching logic. Iterate through each division, fetch its queues, and aggregate them into a single list. This approach ensures that you capture queues from every division, regardless of how the organization is structured.
async function fetchAllQueuesAcrossDivisions() {
const allDivisions = await getAllDivisions();
const allQueues = [];
for (const division of allDivisions) {
console.log(`Fetching queues for division: ${division.id} (${division.name})`);
try {
const divisionQueues = await getQueuesForDivision(division.id);
allQueues.push(...divisionQueues);
console.log(` Found ${divisionQueues.length} queues.`);
} catch (error) {
console.error(` Failed to fetch queues for division ${division.id}. Skipping.`);
// Decide whether to fail fast or continue. Here we continue.
}
}
return allQueues;
}
Complete Working Example
The following script combines all steps into a single, runnable module. It includes error handling, logging, and exports the final list of queues.
const { PlatformClient } = require('genesys-cloud');
require('dotenv').config();
// Initialize the platform client
const platformClient = new PlatformClient();
// Configure the environment
const region = process.env.GENESYS_CLOUD_REGION || 'us-east-1';
platformClient.setEnvironment(region);
// Set the OAuth client credentials
platformClient.setAuthClientCredentials(
process.env.GENESYS_CLOUD_CLIENT_ID,
process.env.GENESYS_CLOUD_CLIENT_SECRET
);
/**
* Fetches all divisions accessible to the authenticated client.
* @returns {Promise<Array>} A list of division objects.
*/
async function getAllDivisions() {
try {
const divisionsApi = platformClient.DivisionsApi;
// Fetch all divisions. Using a large page size to reduce round trips.
const response = await divisionsApi.getOrganizationsDivisions({
pageSize: 100,
pageNumber: 1
});
if (!response.body || !response.body.entities) {
console.error('No divisions found or invalid response structure.');
return [];
}
return response.body.entities;
} catch (error) {
console.error('Error fetching divisions:', error.message);
throw error;
}
}
/**
* Fetches all queues for a specific division ID, handling pagination.
* @param {string} divisionId - The ID of the division.
* @returns {Promise<Array>} A list of queue objects.
*/
async function getQueuesForDivision(divisionId) {
const routingApi = platformClient.RoutingApi;
const allQueues = [];
let pageNumber = 1;
const pageSize = 100;
let hasMorePages = true;
while (hasMorePages) {
try {
const response = await routingApi.getRoutingQueues({
divisionId: divisionId,
pageSize: pageSize,
pageNumber: pageNumber
});
const queues = response.body.entities;
if (!queues || queues.length === 0) {
hasMorePages = false;
break;
}
allQueues.push(...queues);
// If the number of items returned is less than the page size,
// we have reached the last page.
if (queues.length < pageSize) {
hasMorePages = false;
} else {
pageNumber++;
}
} catch (error) {
console.error(`Error fetching queues for division ${divisionId}:`, error.message);
throw error;
}
}
return allQueues;
}
/**
* Main function to fetch all queues across all divisions.
*/
async function main() {
console.log('Starting queue fetch process...');
try {
// Step 1: Get all divisions
const allDivisions = await getAllDivisions();
console.log(`Found ${allDivisions.length} divisions.`);
if (allDivisions.length === 0) {
console.log('No divisions to process. Exiting.');
return;
}
// Step 2: Aggregate queues from all divisions
const allQueues = [];
for (const division of allDivisions) {
console.log(`Processing division: ${division.name} (ID: ${division.id})`);
const divisionQueues = await getQueuesForDivision(division.id);
allQueues.push(...divisionQueues);
}
// Step 3: Output results
console.log(`\nTotal queues fetched: ${allQueues.length}`);
// Example: Print a summary of queues
allQueues.forEach(queue => {
console.log(`- Queue: ${queue.name} | ID: ${queue.id} | Division: ${queue.divisionId}`);
});
return allQueues;
} catch (error) {
console.error('Fatal error during execution:', error);
process.exit(1);
}
}
// Run the main function
main();
Common Errors & Debugging
Error: 401 Unauthorized
Cause: The OAuth client credentials are invalid, expired, or the client has been disabled.
Fix: Verify that GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET in your .env file match the values in the Genesys Cloud Admin UI. Ensure the client status is “Active”.
// Debugging tip: Check the token validity
const token = platformClient.getAccessToken();
console.log('Current Token:', token);
Error: 403 Forbidden
Cause: The OAuth client lacks the required routing:queue:read scope.
Fix: Go to Settings > Security > OAuth Client, select your client, and ensure routing:queue:read is checked under Scopes. Save the changes. The SDK will automatically request a new token with the updated scopes on the next call.
Error: 429 Too Many Requests
Cause: You have exceeded the rate limit for the API. The Genesys Cloud API enforces rate limits per client ID.
Fix: Implement exponential backoff. The SDK does not automatically retry 429 errors. You must catch the error and wait before retrying.
async function fetchWithRetry(apiCall, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
return await apiCall();
} catch (error) {
if (error.status === 429) {
const waitTime = Math.pow(2, i) * 1000; // Exponential backoff
console.log(`Rate limited. Waiting ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
// Usage
// const response = await fetchWithRetry(() => routingApi.getRoutingQueues(params));
Error: Division Not Found or Empty List
Cause: The authenticated user or client does not have access to all divisions in the organization.
Fix: Check the user profile associated with the OAuth client. Ensure the user has administrative or read access to the divisions you expect to see. If using a service account, ensure it is added to the relevant divisions.