CXone Studio SNIPPET Action — How to Make a REST API Call and Parse the JSON Response

CXone Studio SNIPPET Action — How to Make a REST API Call and Parse the JSON Response

What You Will Build

  • One sentence: You will build a CXone Studio flow that uses a JavaScript SNIPPET action to execute an authenticated HTTP GET request to an external REST API and parse the resulting JSON payload into flow variables.
  • One sentence: This tutorial uses the CXone Studio environment and the fetch API available within the SNIPPET execution context.
  • One sentence: The code is written in JavaScript (ES6+), which is the native language for CXone Studio SNIPPET actions.

Prerequisites

  • CXone Environment: Access to a CXone instance with Studio enabled.
  • Permissions: You must have permissions to create and publish Studio flows.
  • External API Target: A publicly accessible or internally accessible REST endpoint. For this tutorial, we will use a mock API endpoint (https://jsonplaceholder.typicode.com/users/1) that returns deterministic JSON data without requiring authentication on the target side. If your target API requires authentication, you must manage those credentials within the SNIPPET or via a secure variable.
  • Studio Flow: A new or existing Studio flow with a trigger (e.g., Incoming Call or API Trigger).

Authentication Setup

CXone Studio SNIPPET actions run within a secure sandboxed environment. Unlike server-side integrations where you manage OAuth tokens in a database, the SNIPPET context handles the execution environment. However, if the target REST API requires authentication, you must include that logic in the JavaScript.

For this tutorial, we assume the target API is public. If you were calling a protected API, you would typically:

  1. Store the API Key or Client Secret in a Secure Variable in CXone Studio.
  2. Retrieve it in the SNIPPET using the context object.

Example of accessing a secure variable in a SNIPPET:

const apiKey = context.getVariable('secure_api_key');

Implementation

Step 1: Configure the SNIPPET Action

First, add a SNIPPET action to your Studio flow.

  1. Drag the SNIPPET component into your flow canvas.
  2. Name it FetchExternalData.
  3. In the Input Variables section, define any variables you need to pass into the script (e.g., userId). For this tutorial, we will hardcode the user ID for simplicity, but in production, you would map a flow variable here.
  4. In the Output Variables section, define the variables where you will store the parsed JSON data. We will create:
    • apiResponseData (Type: JSON/Object)
    • apiResponseStatus (Type: Number)
    • apiErrorMessage (Type: String)

Step 2: Core Logic — The Fetch Request

The core of the SNIPPET action is the JavaScript code block. You must use the async/await pattern because HTTP requests are asynchronous. The CXone Studio SNIPPET engine supports modern JavaScript features, including fetch.

Below is the production-quality code. It includes error handling for network failures, non-200 HTTP status codes, and JSON parsing errors.

// Define the target URL
const targetUrl = 'https://jsonplaceholder.typicode.com/users/1';

let responseStatus = 0;
let errorMessage = '';
let responseData = null;

try {
    // 1. Execute the HTTP GET request
    // Note: Do not use 'await' at the top level if the environment does not support top-level await.
    // In CXone Studio SNIPPETs, you typically wrap logic in an async function or return a promise.
    // However, the SNIPPET editor often allows direct async execution.
    
    const response = await fetch(targetUrl, {
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            // 'Authorization': 'Bearer ' + apiKey // Uncomment if using auth
        },
        // Timeout is critical in call flows to prevent hanging. 
        // Note: Standard fetch does not have a built-in timeout option in all environments.
        // If supported, add: signal: AbortSignal.timeout(5000)
    });

    // 2. Check HTTP Status
    if (!response.ok) {
        // Handle HTTP errors (4xx, 5xx)
        responseStatus = response.status;
        errorMessage = `HTTP Error: ${response.status} ${response.statusText}`;
        
        // Attempt to read the error body if available
        try {
            const errorBody = await response.text();
            errorMessage += ` | Body: ${errorBody}`;
        } catch (e) {
            // Ignore body read error
        }
        
        throw new Error(errorMessage);
    }

    // 3. Parse JSON
    responseStatus = response.status;
    const textData = await response.text();
    
    if (!textData) {
        errorMessage = 'Empty response body';
        throw new Error(errorMessage);
    }

    responseData = JSON.parse(textData);

} catch (error) {
    // 4. Handle Network or Parsing Errors
    if (!errorMessage) {
        errorMessage = error.message || 'Unknown error occurred during fetch';
    }
    console.error('SNIPPET ERROR:', errorMessage);
}

// 5. Assign results to Output Variables
// The context object allows you to set variables that will be available in subsequent flow actions.
context.setVariable('apiResponseStatus', responseStatus);
context.setVariable('apiErrorMessage', errorMessage);
context.setVariable('apiResponseData', responseData);

// Return statement is optional in some CXone versions, but good practice to indicate completion
return {
    status: responseStatus,
    error: errorMessage,
    data: responseData
};

Explanation of Non-Obvious Parameters

  • await fetch(...): The fetch function returns a Promise. In the CXone Studio SNIPPET context, the execution thread must wait for the network call to complete before proceeding to the next flow action. Using await ensures the flow does not move to the next step until the API call finishes.
  • response.ok: This boolean is true only for status codes in the range 200-299. It is the standard way to check for HTTP success before attempting to parse the body.
  • context.setVariable(...): This is the bridge between the JavaScript SNIPPET and the Studio Flow. Any data parsed in JavaScript must be explicitly written to a flow variable using this method to be used in later actions (like a “Set Variable” or “Play Message”).

Step 3: Processing Results and Edge Cases

After the SNIPPET action executes, the flow continues. You must handle the case where the API call failed.

  1. Add a Decision Action immediately after the SNIPPET.
  2. Condition: Check if apiResponseStatus is equal to 200.
  3. True Path: The API call succeeded. You can now access properties from apiResponseData.
    • Example: Drag a Set Variable action.
    • Set callerName to apiResponseData.name.
    • Set callerEmail to apiResponseData.email.
  4. False Path: The API call failed.
    • Example: Drag a Play Message action.
    • Say: “We are currently experiencing technical difficulties. Please try again later.”
    • Alternatively, log the apiErrorMessage to a database or send it to an admin email.

Edge Case: Timeout Handling

If your external API is slow, the SNIPPET action will hang, potentially causing the call to drop or the flow to time out. The standard fetch API in many JavaScript environments does not support a native timeout option in the init object.

To handle timeouts in CXone Studio SNIPPETs, you should wrap the fetch call in a Promise.race or use an AbortController if supported by the specific runtime version of your CXone instance.

Here is the timeout-safe version of the fetch logic:

const targetUrl = 'https://jsonplaceholder.typicode.com/users/1';
const timeoutMs = 3000; // 3 seconds

let responseStatus = 0;
let errorMessage = '';
let responseData = null;

try {
    // Create an AbortController to handle timeout
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

    const response = await fetch(targetUrl, {
        method: 'GET',
        headers: {
            'Accept': 'application/json'
        },
        signal: controller.signal
    });

    // Clear timeout as request completed
    clearTimeout(timeoutId);

    if (!response.ok) {
        responseStatus = response.status;
        errorMessage = `HTTP Error: ${response.status} ${response.statusText}`;
        throw new Error(errorMessage);
    }

    responseStatus = response.status;
    const textData = await response.text();
    
    if (!textData) {
        errorMessage = 'Empty response body';
        throw new Error(errorMessage);
    }

    responseData = JSON.parse(textData);

} catch (error) {
    if (error.name === 'AbortError') {
        errorMessage = `Request timed out after ${timeoutMs}ms`;
    } else {
        errorMessage = error.message || 'Unknown error occurred';
    }
    console.error('SNIPPET ERROR:', errorMessage);
}

context.setVariable('apiResponseStatus', responseStatus);
context.setVariable('apiErrorMessage', errorMessage);
context.setVariable('apiResponseData', responseData);

Complete Working Example

Below is the full, copy-pasteable JavaScript code for the SNIPPET action. This example fetches user data, handles timeouts, parses JSON, and sets flow variables.

Instructions:

  1. Create a new Studio Flow.
  2. Add a SNIPPET action.
  3. Define Output Variables: apiResponseStatus (Number), apiErrorMessage (String), apiResponseData (JSON).
  4. Paste the following code into the SNIPPET editor.
/**
 * CXone Studio SNIPPET: Fetch External JSON Data
 * 
 * This script performs a GET request to an external API,
 * handles timeouts and errors, and stores the result in flow variables.
 */

const TARGET_URL = 'https://jsonplaceholder.typicode.com/users/1';
const TIMEOUT_MS = 3000;

let responseStatus = 0;
let errorMessage = '';
let responseData = null;

try {
    // Initialize AbortController for timeout management
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);

    // Execute Fetch Request
    const response = await fetch(TARGET_URL, {
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        signal: controller.signal
    });

    // Clear timeout since request completed
    clearTimeout(timeoutId);

    // Check HTTP Status
    if (!response.ok) {
        responseStatus = response.status;
        errorMessage = `HTTP Error: ${response.status} ${response.statusText}`;
        
        // Try to get more details from error body
        try {
            const errBody = await response.text();
            if (errBody) {
                errorMessage += ` | Details: ${errBody.substring(0, 100)}`;
            }
        } catch (e) {
            // Ignore body parsing errors
        }
        
        throw new Error(errorMessage);
    }

    // Parse JSON Response
    responseStatus = response.status;
    const textContent = await response.text();

    if (!textContent || textContent.trim() === '') {
        errorMessage = 'Received empty response body';
        throw new Error(errorMessage);
    }

    try {
        responseData = JSON.parse(textContent);
    } catch (parseError) {
        errorMessage = `JSON Parse Error: ${parseError.message}`;
        throw new Error(errorMessage);
    }

} catch (error) {
    // Handle Timeout or Other Errors
    if (error.name === 'AbortError') {
        errorMessage = `Request timed out after ${TIMEOUT_MS}ms`;
    } else if (!errorMessage) {
        errorMessage = error.message || 'An unexpected error occurred';
    }
    
    // Log to CXone console for debugging
    console.error('SNIPPET_FAILURE:', errorMessage);
}

// Set Output Variables for the Flow
context.setVariable('apiResponseStatus', responseStatus);
context.setVariable('apiErrorMessage', errorMessage);
context.setVariable('apiResponseData', responseData);

// Optional: Return object for debugging in some CXone versions
return {
    status: responseStatus,
    error: errorMessage,
    data: responseData
};

Common Errors & Debugging

Error: AbortError or Request timed out

  • What causes it: The external API took longer than TIMEOUT_MS (3 seconds in the example) to respond. This is common with poorly performing endpoints or network latency.
  • How to fix it:
    1. Increase the TIMEOUT_MS value in the script if the API is legitimately slow.
    2. Optimize the external API endpoint.
    3. Ensure your CXone instance has network connectivity to the target URL. Some CXone regions may block outbound traffic to certain ports or IPs.

Error: JSON Parse Error: Unexpected token

  • What causes it: The API returned a 200 OK status, but the body was not valid JSON (e.g., HTML error page, plain text, or XML).
  • How to fix it:
    1. Check the apiErrorMessage variable in the flow. It will contain the parsing error details.
    2. Inspect the response.text() before parsing. You can temporarily add console.log(textContent) to see the raw response.
    3. Ensure the Accept: application/json header is set, as some APIs return different content types based on this header.

Error: TypeError: fetch is not a function

  • What causes it: Older versions of CXone Studio or specific runtime configurations may not support the modern fetch API.
  • How to fix it:
    1. Update your CXone instance to the latest release.
    2. If fetch is unavailable, you may need to use a different integration method, such as a REST API action component in Studio (if available in your region) or a Server-Side integration via a webhook. Note: Native SNIPPET support for fetch is standard in modern CXone Studio environments.

Error: ReferenceError: context is not defined

  • What causes it: The SNIPPET is running in a local JavaScript editor rather than the CXone Studio environment.
  • How to fix it: The context object is injected by the CXone runtime. You cannot test this code in a browser console or Node.js REPL directly. You must publish the flow and trigger it to test. For local debugging, mock the context object:
    // Mock context for local testing ONLY
    const context = {
        setVariable: (name, value) => console.log(`Set ${name}:`, value),
        getVariable: (name) => undefined
    };
    

Official References