How to Initiate a Cobrowse Session Programmatically via Genesys Cloud CX API

How to Initiate a Cobrowse Session Programmatically via Genesys Cloud CX API

What You Will Build

You will build a backend service that initiates a cobrowse session for a specific user or agent within Genesys Cloud CX.
This tutorial uses the Genesys Cloud CX Conversations API and the cobrowse resource group.
The implementation is provided in Python using the official genesyscloud SDK and raw HTTP requests for transparency.

Prerequisites

  • OAuth Client Credentials: You need a Genesys Cloud application with the client_credentials grant type.
  • Required OAuth Scopes:
    • cobrowse:session:write (to initiate the session)
    • cobrowse:session:read (to retrieve session status/details)
    • user:read (if you need to resolve user IDs from emails)
  • SDK Version: genesyscloud Python SDK version 145.0.0 or higher.
  • Runtime: Python 3.9+.
  • Dependencies:
    • genesyscloud
    • requests (for raw HTTP examples)

Install the dependencies:

pip install genesyscloud requests

Authentication Setup

Genesys Cloud CX uses OAuth 2.0. For server-to-server integrations, the client_credentials flow is the standard. You must exchange your client ID and client secret for an access token before making any API calls.

The following Python class handles token acquisition and caching. It ensures you do not hit rate limits by requesting new tokens unnecessarily.

import requests
import time
import os
from typing import Optional, Dict

class GenesysAuth:
    def __init__(self, client_id: str, client_secret: str, environment: str = "mypurecloud.com"):
        self.client_id = client_id
        self.client_secret = client_secret
        self.environment = environment
        self.base_url = f"https://{environment}"
        self.token_url = f"{self.base_url}/oauth/token"
        self.access_token: Optional[str] = None
        self.token_expiry: float = 0

    def get_access_token(self) -> str:
        """
        Retrieves an OAuth access token.
        Returns a cached token if it is still valid.
        """
        # Check if token is still valid (add 60s buffer for clock skew)
        if self.access_token and time.time() < (self.token_expiry - 60):
            return self.access_token

        # Request new token
        headers = {
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = {
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        }

        try:
            response = requests.post(self.token_url, headers=headers, data=data)
            response.raise_for_status()
            token_data = response.json()
            
            self.access_token = token_data["access_token"]
            self.token_expiry = time.time() + token_data["expires_in"]
            
            return self.access_token
        except requests.exceptions.HTTPError as e:
            raise Exception(f"Failed to authenticate with Genesys Cloud: {e.response.text}")
        except Exception as e:
            raise Exception(f"Authentication error: {str(e)}")

# Usage Example
# auth = GenesysAuth(
#     client_id=os.getenv("GENESYS_CLIENT_ID"),
#     client_secret=os.getenv("GENESYS_CLIENT_SECRET")
# )
# token = auth.get_access_token()

Implementation

Step 1: Identify the Target Participant

To initiate a cobrowse session, you must target a specific participant. In Genesys Cloud, cobrowse sessions are typically initiated by an agent toward a customer, or by a system toward an agent.

The API requires the userId of the participant who will be the target of the cobrowse session. If you only have an email address or a phone number, you must first resolve this to a userId.

Here is how to resolve a user ID using the Users API.

Endpoint: GET /api/v2/users

Required Scope: user:read

import requests

def find_user_by_email(auth: GenesysAuth, email: str) -> Optional[str]:
    """
    Resolves a Genesys Cloud User ID by email address.
    """
    url = f"https://{auth.environment}/api/v2/users"
    headers = {
        "Authorization": f"Bearer {auth.get_access_token()}",
        "Content-Type": "application/json"
    }
    
    # Query parameters for filtering
    params = {
        "email": email,
        "pageSize": 10
    }

    try:
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()
        
        users = response.json().get("entities", [])
        if not users:
            return None
        
        # Return the first matching user ID
        return users[0]["id"]
    except requests.exceptions.HTTPError as e:
        if response.status_code == 404:
            return None
        raise Exception(f"Error fetching user: {e.response.text}")

# Example
# user_id = find_user_by_email(auth, "customer@example.com")
# if not user_id:
#     raise ValueError("User not found in Genesys Cloud.")

Step 2: Initiate the Cobrowse Session

The core action is invoking the POST /api/v2/cobrowse/sessions endpoint. This endpoint creates a new cobrowse session and associates it with the target user.

Critical Parameters:

  • userId: The ID of the participant to cobrowse with.
  • initiatorId: The ID of the user initiating the session (usually the agent or the application user). If omitted, the system may assign a default or fail depending on configuration.
  • type: The type of cobrowse session. Common values are standard or remote.

Endpoint: POST /api/v2/cobrowse/sessions

Required Scope: cobrowse:session:write

import requests
import json

def initiate_cobrowse_session(auth: GenesysAuth, target_user_id: str, initiator_user_id: str) -> Dict:
    """
    Initiates a new cobrowse session in Genesys Cloud.
    
    Args:
        auth: GenesysAuth instance
        target_user_id: The ID of the user to cobrowse with
        initiator_user_id: The ID of the user initiating the session
    
    Returns:
        Dict containing the session details
    """
    url = f"https://{auth.environment}/api/v2/cobrowse/sessions"
    headers = {
        "Authorization": f"Bearer {auth.get_access_token()}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "userId": target_user_id,
        "initiatorId": initiator_user_id,
        "type": "standard"
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        
        # Handle common errors
        if response.status_code == 401:
            raise Exception("Unauthorized. Check your OAuth token and scopes.")
        elif response.status_code == 403:
            raise Exception("Forbidden. Ensure the client has 'cobrowse:session:write' scope.")
        elif response.status_code == 404:
            raise Exception(f"User ID {target_user_id} not found or not eligible for cobrowse.")
        elif response.status_code == 429:
            # Implement retry logic here in production
            raise Exception("Rate limit exceeded. Please wait and retry.")
        
        response.raise_for_status()
        return response.json()
    
    except requests.exceptions.HTTPError as e:
        raise Exception(f"Failed to initiate cobrowse session: {e.response.text}")

# Example
# session_data = initiate_cobrowse_session(auth, user_id, "agent-user-id-123")
# session_id = session_data["id"]

Expected Response Body:

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "userId": "target-user-id-456",
  "initiatorId": "agent-user-id-123",
  "type": "standard",
  "state": "active",
  "createdTimestamp": "2023-10-27T10:00:00.000Z",
  "updatedTimestamp": "2023-10-27T10:00:00.000Z",
  "links": {
    "self": {
      "href": "https://mypurecloud.com/api/v2/cobrowse/sessions/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }
  }
}

Step 3: Retrieve the Cobrowse Link for the Client

Once the session is created, you need to provide the target user with a way to join. Genesys Cloud provides a joinUrl or a token that the frontend client uses to initialize the cobrowse SDK.

You can retrieve the session details using the session ID returned in Step 2.

Endpoint: GET /api/v2/cobrowse/sessions/{sessionId}

Required Scope: cobrowse:session:read

def get_cobrowse_join_url(auth: GenesysAuth, session_id: str) -> str:
    """
    Retrieves the join URL for a specific cobrowse session.
    """
    url = f"https://{auth.environment}/api/v2/cobrowse/sessions/{session_id}"
    headers = {
        "Authorization": f"Bearer {auth.get_access_token()}",
        "Content-Type": "application/json"
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        
        session_data = response.json()
        
        # The join URL is often embedded in the links or attributes depending on the SDK version
        # In many cases, the frontend uses the session ID and a token to join.
        # Check for 'joinUrl' or construct it based on documentation.
        
        if "joinUrl" in session_data:
            return session_data["joinUrl"]
        else:
            # Fallback: Return the session ID for the frontend to handle via SDK
            return session_data["id"]
            
    except requests.exceptions.HTTPError as e:
        raise Exception(f"Failed to get session details: {e.response.text}")

Complete Working Example

This script combines authentication, user resolution, session initiation, and link retrieval into a single workflow.

import os
import time
import requests
from typing import Optional, Dict

# --- Authentication Class (from Step 1) ---
class GenesysAuth:
    def __init__(self, client_id: str, client_secret: str, environment: str = "mypurecloud.com"):
        self.client_id = client_id
        self.client_secret = client_secret
        self.environment = environment
        self.base_url = f"https://{environment}"
        self.token_url = f"{self.base_url}/oauth/token"
        self.access_token: Optional[str] = None
        self.token_expiry: float = 0

    def get_access_token(self) -> str:
        if self.access_token and time.time() < (self.token_expiry - 60):
            return self.access_token

        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        data = {
            "grant_type": "client_credentials",
            "client_id": self.client_id,
            "client_secret": self.client_secret
        }

        try:
            response = requests.post(self.token_url, headers=headers, data=data)
            response.raise_for_status()
            token_data = response.json()
            self.access_token = token_data["access_token"]
            self.token_expiry = time.time() + token_data["expires_in"]
            return self.access_token
        except requests.exceptions.HTTPError as e:
            raise Exception(f"Authentication failed: {e.response.text}")

# --- Helper Functions ---

def find_user_by_email(auth: GenesysAuth, email: str) -> Optional[str]:
    url = f"https://{auth.environment}/api/v2/users"
    headers = {"Authorization": f"Bearer {auth.get_access_token()}"}
    params = {"email": email, "pageSize": 10}
    
    try:
        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()
        users = response.json().get("entities", [])
        return users[0]["id"] if users else None
    except requests.exceptions.HTTPError:
        return None

def initiate_cobrowse_session(auth: GenesysAuth, target_user_id: str, initiator_user_id: str) -> Dict:
    url = f"https://{auth.environment}/api/v2/cobrowse/sessions"
    headers = {
        "Authorization": f"Bearer {auth.get_access_token()}",
        "Content-Type": "application/json"
    }
    payload = {
        "userId": target_user_id,
        "initiatorId": initiator_user_id,
        "type": "standard"
    }
    
    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        raise Exception(f"Initiation failed: {e.response.text}")

def get_cobrowse_details(auth: GenesysAuth, session_id: str) -> Dict:
    url = f"https://{auth.environment}/api/v2/cobrowse/sessions/{session_id}"
    headers = {"Authorization": f"Bearer {auth.get_access_token()}"}
    
    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        raise Exception(f"Retrieval failed: {e.response.text}")

# --- Main Execution Flow ---

def main():
    # Configuration
    CLIENT_ID = os.getenv("GENESYS_CLIENT_ID", "your-client-id")
    CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET", "your-client-secret")
    TARGET_EMAIL = os.getenv("TARGET_USER_EMAIL", "customer@example.com")
    INITIATOR_USER_ID = os.getenv("INITIATOR_USER_ID", "your-agent-user-id")
    
    # 1. Authenticate
    auth = GenesysAuth(CLIENT_ID, CLIENT_SECRET)
    print("Authenticating...")
    token = auth.get_access_token()
    print(f"Authenticated. Token obtained.")

    # 2. Resolve Target User
    print(f"Resolving user ID for {TARGET_EMAIL}...")
    target_user_id = find_user_by_email(auth, TARGET_EMAIL)
    
    if not target_user_id:
        print(f"Error: User with email {TARGET_EMAIL} not found in Genesys Cloud.")
        return

    print(f"Resolved User ID: {target_user_id}")

    # 3. Initiate Cobrowse Session
    print("Initiating cobrowse session...")
    try:
        session_data = initiate_cobrowse_session(auth, target_user_id, INITIATOR_USER_ID)
        session_id = session_data["id"]
        print(f"Session initiated. ID: {session_id}")
    except Exception as e:
        print(f"Error initiating session: {e}")
        return

    # 4. Retrieve Session Details
    print("Retrieving session details...")
    try:
        details = get_cobrowse_details(auth, session_id)
        print("Session Details:")
        print(f"  State: {details.get('state')}")
        print(f"  Created: {details.get('createdTimestamp')}")
        
        # In a real application, you would send the session_id or join_url to the frontend
        # to initialize the Genesys Cloud Cobrowse SDK.
        
    except Exception as e:
        print(f"Error retrieving details: {e}")

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 401 Unauthorized

Cause: The OAuth token is invalid, expired, or missing.
Fix:

  1. Verify that your client_id and client_secret are correct.
  2. Ensure the token has not expired. The GenesysAuth class handles refresh, but if you are using raw tokens, check the expires_in field.
  3. Confirm the token is included in the Authorization: Bearer <token> header.

Error: 403 Forbidden

Cause: The OAuth client does not have the required scopes.
Fix:

  1. Go to the Genesys Cloud Admin Console > Platform > Apps.
  2. Edit your application.
  3. Ensure cobrowse:session:write and cobrowse:session:read are checked under OAuth Scopes.
  4. Save and regenerate the client secret if necessary.

Error: 404 Not Found

Cause: The userId provided does not exist or is not a valid Genesys Cloud user.
Fix:

  1. Verify the userId is correct.
  2. Ensure the user is active in Genesys Cloud.
  3. If using an email to resolve the user, ensure the email matches exactly with the user’s profile.

Error: 429 Too Many Requests

Cause: You have exceeded the API rate limit.
Fix:

  1. Implement exponential backoff in your retry logic.
  2. Cache tokens and session data to reduce API calls.
  3. Check the Retry-After header in the response for the recommended wait time.
import time
import random

def retry_with_backoff(func, *args, max_retries=3, **kwargs):
    for attempt in range(max_retries):
        try:
            return func(*args, **kwargs)
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                wait_time = 2 ** attempt + random.uniform(0, 1)
                print(f"Rate limited. Waiting {wait_time:.2f} seconds...")
                time.sleep(wait_time)
            else:
                raise
    raise Exception("Max retries exceeded")

Official References