How to Pass CRM Customer ID Through the Genesys Cloud Web Messaging SDK startChat() Method

How to Pass CRM Customer ID Through the Genesys Cloud Web Messaging SDK startChat() Method

What You Will Build

  • You will initialize a Genesys Cloud Web Messaging session and inject a custom CRM Customer ID into the conversation context before the chat begins.
  • This tutorial uses the Genesys Cloud Web Messaging SDK (@genesys/web-messaging-sdk) and the Genesys Cloud REST API for verification.
  • The implementation is demonstrated in JavaScript (Node.js environment for backend initiation or browser-side logic) and Python for API validation.

Prerequisites

  • OAuth Client Type: A Genesys Cloud OAuth Client with the following scopes:
    • webchat:read (to verify conversation attributes via API)
    • conversation:webchat:read (to access conversation details)
    • user:read (optional, for debugging user context)
  • SDK Version: @genesys/web-messaging-sdk version 2.x or later.
  • Language/Runtime: Node.js 18+ for the frontend SDK simulation; Python 3.9+ for the backend verification script.
  • External Dependencies:
    • @genesys/web-messaging-sdk (npm)
    • requests (pip) for the Python verification script.
    • dotenv (npm) for managing environment variables in the Node.js example.

Authentication Setup

The Web Messaging SDK handles its own internal authentication with the Genesys Cloud Web Messaging service. However, to verify that the CRM ID was successfully passed, you must authenticate against the Genesys Cloud REST API using a Service Account OAuth client.

You will need two distinct authentication mechanisms in this tutorial:

  1. Frontend SDK Auth: Handled automatically by the SDK using your deploymentId and region.
  2. Backend API Auth: A standard OAuth2 Client Credentials flow to fetch conversation details and prove the attribute persistence.

Python API Authentication Helper

This helper establishes the session used later to verify the CRM ID.

import requests
import os
from typing import Optional

class GenesysAuth:
    def __init__(self, org_id: str, client_id: str, client_secret: str, region: str = "mypurecloud.com"):
        self.org_id = org_id
        self.client_id = client_id
        self.client_secret = client_secret
        self.region = region
        self.token_url = f"https://login.{region}/oauth/token"
        self.api_base_url = f"https://api.{region}"
        self.access_token: Optional[str] = None
        self.expires_at: Optional[int] = None

    def get_token(self) -> str:
        """
        Retrieves an OAuth2 access token using Client Credentials flow.
        """
        payload = {
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        }
        
        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }

        try:
            response = requests.post(self.token_url, data=payload, headers=headers)
            response.raise_for_status()
            data = response.json()
            self.access_token = data["access_token"]
            self.expires_at = int(data["expires_in"])
            return self.access_token
        except requests.exceptions.HTTPError as e:
            print(f"Authentication failed: {e.response.status_code} - {e.response.text}")
            raise
        except Exception as e:
            print(f"Unexpected error during authentication: {e}")
            raise

    def get_headers(self) -> dict:
        """
        Returns the required headers for API calls.
        """
        if not self.access_token:
            self.get_token()
        
        return {
            "Authorization": f"Bearer {self.access_token}",
            "Content-Type": "application/json",
            "Accept": "application/json"
        }

# Usage Example
# auth = GenesysAuth(
#     org_id=os.getenv("GENESYS_ORG_ID"),
#     client_id=os.getenv("GENESYS_CLIENT_ID"),
#     client_secret=os.getenv("GENESYS_CLIENT_SECRET")
# )
# auth.get_token()

Implementation

Step 1: Initialize the Web Messaging SDK with Custom Attributes

The core mechanism for passing external data (like a CRM Customer ID) into a Genesys Cloud conversation is the startChat method. This method accepts an object containing attributes. These attributes are merged into the conversation’s metadata and are available to routing rules, IVR scripts, and post-conversation analytics.

Critical Note: The attributes object is strictly typed as a dictionary of strings. You cannot pass complex objects or arrays directly in the top-level startChat attributes without serialization.

JavaScript Implementation (Browser/Node Context)

This example demonstrates how to configure the SDK and trigger startChat with the CRM ID.

import { WebMessagingSDK } from '@genesys/web-messaging-sdk';

/**
 * Configuration for the Web Messaging SDK.
 * Replace these values with your actual Genesys Cloud deployment details.
 */
const SDK_CONFIG = {
    deploymentId: 'YOUR_DEPLOYMENT_ID', // e.g., 'a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8'
    region: 'mypurecloud.com', // e.g., 'mypurecloud.ie' for EU
    locale: 'en-US'
};

/**
 * CRM Customer ID to be passed into the conversation.
 * In a real application, this might come from a user session, cookie, or local storage.
 */
const CRM_CUSTOMER_ID = 'CRM-USER-987654321';

/**
 * Initializes the Web Messaging SDK and starts a chat with custom attributes.
 */
async function initializeAndStartChat() {
    try {
        // 1. Initialize the SDK
        const sdk = new WebMessagingSDK(SDK_CONFIG);

        // 2. Define the custom attributes to pass.
        // Genesys Cloud supports custom attributes prefixed with 'custom_' or defined in the 
        // deployment configuration. Here we use a standard custom attribute.
        const chatAttributes = {
            // Standard Genesys attribute for identifying the user source
            'custom_crmId': CRM_CUSTOMER_ID,
            // Additional context often useful for routing
            'custom_source': 'web_portal',
            'custom_timestamp': new Date().toISOString()
        };

        console.log(`Starting chat with CRM ID: ${CRM_CUSTOMER_ID}`);

        // 3. Start the chat session.
        // The startChat method returns a Promise that resolves with the conversation ID.
        const conversationId = await sdk.startChat({
            attributes: chatAttributes
        });

        console.log(`Chat started successfully. Conversation ID: ${conversationId}`);
        
        return conversationId;

    } catch (error) {
        // Handle SDK-specific errors
        if (error.code === 'INVALID_DEPLOYMENT') {
            console.error('Invalid Deployment ID. Check your SDK configuration.');
        } else if (error.code === 'RATE_LIMITED') {
            console.error('Rate limited by Genesys Cloud. Wait before retrying.');
        } else {
            console.error('Failed to start chat:', error.message);
        }
        throw error;
    }
}

// Execute the function
initializeAndStartChat().catch(console.error);

Step 2: Verify Attribute Persistence via REST API

Once the chat is started, you must verify that the custom_crmId attribute was actually attached to the conversation. This is done using the Genesys Cloud REST API. The attributes are stored in the attributes field of the conversation entity.

Required Scope: conversation:webchat:read

Python Verification Script

This script takes the conversationId returned by the JavaScript SDK and queries the Genesys Cloud API to inspect the conversation details.

import requests
import json
import os
from typing import Dict, Any

# Assume GenesysAuth class from Step 1 is imported here
# from auth_helper import GenesysAuth

def verify_crm_id(conversation_id: str, auth: GenesysAuth) -> bool:
    """
    Verifies that the CRM ID was successfully passed to the Genesys Cloud conversation.
    
    Args:
        conversation_id: The ID returned by sdk.startChat()
        auth: An instance of GenesysAuth configured with valid credentials
        
    Returns:
        True if the CRM ID is found in the conversation attributes, False otherwise.
    """
    # Endpoint to get conversation details
    endpoint = f"{auth.api_base_url}/api/v2/conversations/webchat/details/{conversation_id}"
    
    headers = auth.get_headers()
    
    try:
        # GET request to fetch conversation details
        response = requests.get(endpoint, headers=headers)
        
        # Handle HTTP errors
        if response.status_code == 404:
            print(f"Conversation {conversation_id} not found. It may have ended or not yet persisted.")
            return False
        elif response.status_code == 429:
            print("Rate limited. Please wait before retrying.")
            return False
        elif response.status_code >= 400:
            print(f"API Error: {response.status_code} - {response.text}")
            return False
            
        response.raise_for_status()
        data: Dict[str, Any] = response.json()
        
        # Extract attributes from the response
        # The structure of /conversations/webchat/details includes a 'participants' array
        # and top-level conversation metadata.
        # Custom attributes passed via startChat are typically found in the 
        # 'attributes' object of the conversation or the initial participant.
        
        conversation_attributes = data.get('attributes', {})
        
        # Check for the CRM ID
        crm_id_value = conversation_attributes.get('custom_crmId')
        
        if crm_id_value:
            print(f"Success! Found CRM ID in conversation: {crm_id_value}")
            return True
        else:
            print("CRM ID not found in conversation attributes.")
            print("Available attributes:", json.dumps(conversation_attributes, indent=2))
            return False
            
    except requests.exceptions.RequestException as e:
        print(f"Network error during verification: {e}")
        return False

# Example Usage
# auth = GenesysAuth(...)
# auth.get_token()
# verify_crm_id("abc-123-def-456", auth)

Step 3: Handling Dynamic CRM IDs in a React Component

In a production environment, you rarely hardcode the CRM ID. It is usually available in the user’s session. Below is a React component pattern that safely initializes the SDK and passes the ID only when it is available.

import React, { useState, useEffect } from 'react';
import { WebMessagingSDK } from '@genesys/web-messaging-sdk';

const GenesysChatButton = ({ deploymentId, region, currentUser }) => {
    const [sdk, setSdk] = useState(null);
    const [isChatActive, setIsChatActive] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        // Initialize SDK once on mount
        const initSdk = async () => {
            try {
                const sdkInstance = new WebMessagingSDK({
                    deploymentId,
                    region,
                    locale: 'en-US'
                });
                setSdk(sdkInstance);
            } catch (err) {
                setError('Failed to initialize chat SDK');
                console.error(err);
            }
        };
        
        initSdk();

        // Cleanup on unmount
        return () => {
            if (sdk) {
                // SDK cleanup logic if necessary
            }
        };
    }, [deploymentId, region]);

    const handleStartChat = async () => {
        if (!sdk || !currentUser) {
            setError('User not authenticated or SDK not ready.');
            return;
        }

        try {
            // Construct attributes dynamically
            const attributes = {
                custom_crmId: currentUser.crmId,
                custom_userName: currentUser.name,
                custom_userEmail: currentUser.email,
                custom_tier: currentUser.tier // e.g., 'Gold', 'Silver'
            };

            const conversationId = await sdk.startChat({
                attributes: attributes
            });

            setIsChatActive(true);
            console.log(`Chat started with ID: ${conversationId}`);
            
            // Optional: Dispatch event to parent component to update UI
            // dispatch({ type: 'CHAT_STARTED', payload: { conversationId } });

        } catch (err) {
            setError('Could not start chat session.');
            console.error(err);
        }
    };

    if (error) {
        return <div className="error-message">{error}</div>;
    }

    return (
        <button 
            onClick={handleStartChat} 
            disabled={!currentUser || isChatActive}
            className="genesys-chat-btn"
        >
            {isChatActive ? 'Chat Active' : 'Start Support Chat'}
        </button>
    );
};

export default GenesysChatButton;

Complete Working Example

Below is a consolidated Node.js script that simulates the frontend SDK call and immediately verifies the result via the API. This allows for end-to-end testing in a CI/CD pipeline or local development environment without a browser.

Prerequisites:

  1. Install dependencies: npm install @genesys/web-messaging-sdk dotenv
  2. Install Python dependencies: pip install requests
  3. Set environment variables: GENESYS_ORG_ID, GENESYS_CLIENT_ID, GENESYS_CLIENT_SECRET, GENESYS_DEPLOYMENT_ID, GENESYS_REGION.

1. Node.js Script (start-verify-chat.js)

require('dotenv').config();
const { WebMessagingSDK } = require('@genesys/web-messaging-sdk');
const { spawn } = require('child_process');

const CRM_ID = 'TEST-CRM-ID-' + Date.now();

async function main() {
    console.log(`[Node] Starting chat with CRM ID: ${CRM_ID}`);

    const sdk = new WebMessagingSDK({
        deploymentId: process.env.GENESYS_DEPLOYMENT_ID,
        region: process.env.GENESYS_REGION,
        locale: 'en-US'
    });

    try {
        const conversationId = await sdk.startChat({
            attributes: {
                custom_crmId: CRM_ID,
                custom_test_run: 'true'
            }
        });

        console.log(`[Node] Chat started. Conversation ID: ${conversationId}`);

        // Invoke Python verification script
        const pythonProcess = spawn('python', ['verify-chat.py', conversationId, CRM_ID]);

        let output = '';
        let error = '';

        pythonProcess.stdout.on('data', (data) => {
            output += data.toString();
        });

        pythonProcess.stderr.on('data', (data) => {
            error += data.toString();
        });

        pythonProcess.on('close', (code) => {
            console.log(`[Python] Verification finished with code ${code}`);
            if (output) console.log('[Python] Output:\n' + output);
            if (error) console.error('[Python] Errors:\n' + error);
            
            if (code === 0) {
                console.log('[SUCCESS] End-to-End Test Passed.');
                process.exit(0);
            } else {
                console.error('[FAILURE] End-to-End Test Failed.');
                process.exit(1);
            }
        });

    } catch (error) {
        console.error('[Node] Failed to start chat:', error.message);
        process.exit(1);
    }
}

main();

2. Python Verification Script (verify-chat.py)

import requests
import sys
import os
import time

def main():
    if len(sys.argv) != 3:
        print("Usage: python verify-chat.py <conversation_id> <expected_crm_id>")
        sys.exit(1)

    conversation_id = sys.argv[1]
    expected_crm_id = sys.argv[2]

    org_id = os.getenv("GENESYS_ORG_ID")
    client_id = os.getenv("GENESYS_CLIENT_ID")
    client_secret = os.getenv("GENESYS_CLIENT_SECRET")
    region = os.getenv("GENESYS_REGION", "mypurecloud.com")

    # 1. Authenticate
    token_url = f"https://login.{region}/oauth/token"
    auth_payload = {
        "grant_type": "client_credentials",
        "client_id": client_id,
        "client_secret": client_secret
    }

    try:
        auth_response = requests.post(token_url, data=auth_payload)
        auth_response.raise_for_status()
        token = auth_response.json()["access_token"]
    except Exception as e:
        print(f"Auth Error: {e}")
        sys.exit(1)

    # 2. Fetch Conversation Details
    # Wait a few seconds for the conversation to persist in the API
    print(f"Waiting 3 seconds for conversation persistence...")
    time.sleep(3)

    api_url = f"https://api.{region}/api/v2/conversations/webchat/details/{conversation_id}"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }

    try:
        resp = requests.get(api_url, headers=headers)
        resp.raise_for_status()
        data = resp.json()
        
        attributes = data.get("attributes", {})
        found_crm_id = attributes.get("custom_crmId")

        print(f"Expected CRM ID: {expected_crm_id}")
        print(f"Found CRM ID:    {found_crm_id}")

        if found_crm_id == expected_crm_id:
            print("Verification Successful.")
            sys.exit(0)
        else:
            print("Verification Failed. CRM IDs do not match.")
            sys.exit(1)

    except requests.exceptions.HTTPError as e:
        print(f"API Error: {e.response.status_code} - {e.response.text}")
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 401 Unauthorized on API Verification

  • Cause: The OAuth token expired or the Client ID/Secret is incorrect.
  • Fix: Ensure your Python script fetches a fresh token before making the API call. Check that the OAuth Client has the conversation:webchat:read scope enabled in the Genesys Cloud Admin Console.

Error: 429 Rate Limit on startChat

  • Cause: The Genesys Cloud Web Messaging service limits the number of new sessions per minute per deployment.
  • Fix: Implement exponential backoff in your JavaScript SDK call. Do not retry immediately.
async function startChatWithRetry(sdk, attributes, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await sdk.startChat({ attributes });
        } catch (error) {
            if (error.code === 'RATE_LIMITED') {
                const delay = Math.pow(2, i) * 1000; // Exponential backoff
                console.log(`Rate limited. Retrying in ${delay}ms...`);
                await new Promise(resolve => setTimeout(resolve, delay));
            } else {
                throw error;
            }
        }
    }
    throw new Error("Max retries exceeded for startChat");
}

Error: CRM ID Not Found in API Response

  • Cause: The attribute key name does not match exactly. Genesys Cloud is case-sensitive. If you pass custom_crmId in startChat, you must query for custom_crmId in the API.
  • Fix: Verify the exact key used in the JavaScript attributes object matches the key inspected in the Python script. Also, ensure the deployment configuration in Genesys Cloud allows custom attributes. Some restricted deployments may drop unknown attributes.

Error: “Invalid Deployment ID”

  • Cause: The deploymentId provided to the SDK is malformed or does not exist.
  • Fix: Copy the Deployment ID directly from the Genesys Cloud Admin Console under Engagement > Web Messaging > Deployments. Ensure no trailing spaces.

Official References