How to Configure Web Messaging Deployment to Customize Colors and Launcher Position
What You Will Build
You will build a script that retrieves an existing Genesys Cloud Web Messaging deployment, modifies the UI configuration to change the launcher button color and position, and updates the deployment in the cloud.
This tutorial uses the Genesys Cloud CX API v2 endpoint for Web Messaging deployments.
The implementation is provided in Python using the official genesyscloud SDK and requests library for direct API comparison.
Prerequisites
OAuth Client and Scopes
You must create an OAuth Client in the Genesys Cloud Admin Portal. The client requires the following scopes to manage Web Messaging deployments:
webmessaging:deployment:readwebmessaging:deployment:write
Ensure the client is authorized for the specific environment (sandbox or production) where the deployment resides.
SDK and Dependencies
This tutorial uses Python 3.9+. You need to install the following packages:
genesyscloud: The official Genesys Cloud Python SDK.requests: For making HTTP requests (used in the direct API section).
Install them using pip:
pip install genesyscloud requests
Environment Variables
Store your credentials securely. This tutorial assumes you have set the following environment variables:
GENESYS_CLOUD_REGION: e.g.,us-east-1ormypurecloud.comGENESYS_CLOUD_CLIENT_ID: Your OAuth Client IDGENESYS_CLOUD_CLIENT_SECRET: Your OAuth Client Secret
Authentication Setup
Before interacting with the Web Messaging API, you must obtain an access token. The Genesys Cloud API uses OAuth 2.0 Client Credentials flow.
The following function handles token acquisition. In production, you should implement caching to avoid requesting a new token every time, as tokens are valid for one hour.
import os
import requests
from typing import Optional
def get_access_token(region: str, client_id: str, client_secret: str) -> str:
"""
Retrieves an OAuth 2.0 access token from Genesys Cloud.
Args:
region: The Genesys Cloud region (e.g., 'mypurecloud.com').
client_id: The OAuth client ID.
client_secret: The OAuth client secret.
Returns:
The access token string.
Raises:
requests.exceptions.HTTPError: If authentication fails.
"""
# Construct the auth URL based on region
if region == "mypurecloud.com":
auth_url = "https://api.mypurecloud.com"
else:
auth_url = f"https://{region}.mypurecloud.com"
token_url = f"{auth_url}/oauth/token"
payload = {
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
}
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
response = requests.post(token_url, data=payload, headers=headers)
response.raise_for_status()
data = response.json()
access_token = data.get("access_token")
if not access_token:
raise ValueError("Access token not found in response")
return access_token
# Example usage
if __name__ == "__main__":
region = os.getenv("GENESYS_CLOUD_REGION", "mypurecloud.com")
client_id = os.getenv("GENESYS_CLOUD_CLIENT_ID")
client_secret = os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
if not all([client_id, client_secret]):
raise EnvironmentError("Missing environment variables for credentials.")
token = get_access_token(region, client_id, client_secret)
print(f"Token acquired successfully.")
Implementation
Step 1: Retrieve the Existing Deployment
To modify a deployment, you must first fetch its current configuration. You cannot patch a deployment; you must retrieve the full object, modify the specific fields, and update the entire object. This is a standard RESTful pattern in Genesys Cloud.
The endpoint is GET /api/v2/webmessaging/deployments/{deploymentId}.
You need the deploymentId. You can find this in the Admin Portal under Engagement > Web Messaging > Deployments, or by listing all deployments via GET /api/v2/webmessaging/deployments.
Here is how to fetch the deployment using the requests library to show the raw JSON structure:
def fetch_deployment(access_token: str, region: str, deployment_id: str) -> dict:
"""
Fetches the full configuration of a Web Messaging deployment.
Args:
access_token: The OAuth access token.
region: The Genesys Cloud region.
deployment_id: The ID of the deployment to fetch.
Returns:
A dictionary containing the deployment configuration.
"""
if region == "mypurecloud.com":
base_url = "https://api.mypurecloud.com"
else:
base_url = f"https://{region}.mypurecloud.com"
url = f"{base_url}/api/v2/webmessaging/deployments/{deployment_id}"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
# Handle common errors
if response.status_code == 401:
raise Exception("Authentication failed. Check your token.")
elif response.status_code == 403:
raise Exception("Permission denied. Ensure the client has 'webmessaging:deployment:read' scope.")
elif response.status_code == 404:
raise Exception(f"Deployment {deployment_id} not found.")
response.raise_for_status()
return response.json()
# Usage
# deployment_config = fetch_deployment(token, region, "your-deployment-id")
# print(deployment_config)
The response body contains a nested object called uiConfig. This is where all visual customizations live. The structure looks like this:
{
"id": "your-deployment-id",
"name": "Support Chat",
"enabled": true,
"uiConfig": {
"launcher": {
"position": "bottom-right",
"color": "#0066FF",
"hoverColor": "#0052CC",
"iconType": "message"
},
"chatWindow": {
"title": "Support",
"headerColor": "#0066FF",
"backgroundColor": "#FFFFFF"
}
// ... other config
}
}
Step 2: Modify UI Configuration
The uiConfig object contains the launcher and chatWindow objects. To customize the launcher position and color, you modify the launcher object.
Key fields for customization:
launcher.position: Acceptsbottom-left,bottom-right,top-left, ortop-right. Default isbottom-right.launcher.color: Hex code for the launcher button background.launcher.hoverColor: Hex code for the button when hovered.launcher.iconType: Acceptsmessage,headset, orcustom.
Here is the logic to apply changes:
def update_deployment_ui(config: dict, new_color: str, new_position: str) -> dict:
"""
Modifies the deployment configuration dictionary to update UI settings.
Args:
config: The current deployment configuration dictionary.
new_color: The new hex color for the launcher (e.g., '#FF5733').
new_position: The new position for the launcher (e.g., 'bottom-left').
Returns:
The modified configuration dictionary.
"""
# Validate position
valid_positions = ["bottom-left", "bottom-right", "top-left", "top-right"]
if new_position not in valid_positions:
raise ValueError(f"Invalid position: {new_position}. Must be one of {valid_positions}")
# Ensure uiConfig exists
if "uiConfig" not in config:
config["uiConfig"] = {}
if "launcher" not in config["uiConfig"]:
config["uiConfig"]["launcher"] = {}
# Apply changes
config["uiConfig"]["launcher"]["color"] = new_color
config["uiConfig"]["launcher"]["position"] = new_position
# Optional: Set hover color to a darker shade of the new color
# This is a simple heuristic for demonstration
config["uiConfig"]["launcher"]["hoverColor"] = new_color
return config
Step 3: Update the Deployment
To persist the changes, send a PUT request to the same endpoint used for fetching. The body must include the entire deployment configuration, not just the changed fields.
OAuth Scope Required: webmessaging:deployment:write
def save_deployment(access_token: str, region: str, deployment_id: str, config: dict) -> dict:
"""
Updates an existing Web Messaging deployment with new configuration.
Args:
access_token: The OAuth access token.
region: The Genesys Cloud region.
deployment_id: The ID of the deployment to update.
config: The full deployment configuration dictionary.
Returns:
The updated deployment configuration from the API response.
"""
if region == "mypurecloud.com":
base_url = "https://api.mypurecloud.com"
else:
base_url = f"https://{region}.mypurecloud.com"
url = f"{base_url}/api/v2/webmessaging/deployments/{deployment_id}"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
# Send the entire config object
response = requests.put(url, json=config, headers=headers)
# Handle errors
if response.status_code == 401:
raise Exception("Authentication failed.")
elif response.status_code == 403:
raise Exception("Permission denied. Ensure the client has 'webmessaging:deployment:write' scope.")
elif response.status_code == 400:
error_body = response.json()
raise Exception(f"Bad Request: {error_body.get('message', 'Unknown error')}")
elif response.status_code == 429:
raise Exception("Rate limit exceeded. Wait before retrying.")
response.raise_for_status()
return response.json()
Complete Working Example
The following script combines authentication, fetching, modification, and updating. It includes basic error handling and logging.
import os
import sys
import requests
import logging
from typing import Dict, Any
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class WebMessagingConfigurer:
def __init__(self, region: str, client_id: str, client_secret: str):
self.region = region
self.client_id = client_id
self.client_secret = client_secret
self.access_token = None
# Determine base URL
if region == "mypurecloud.com":
self.base_url = "https://api.mypurecloud.com"
else:
self.base_url = f"https://{region}.mypurecloud.com"
def authenticate(self) -> None:
"""Acquires OAuth access token."""
logger.info("Authenticating with Genesys Cloud...")
token_url = f"{self.base_url}/oauth/token"
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(token_url, data=payload, headers=headers, timeout=10)
response.raise_for_status()
self.access_token = response.json().get("access_token")
if not self.access_token:
raise ValueError("No access token in response")
logger.info("Authentication successful.")
except requests.exceptions.RequestException as e:
logger.error(f"Authentication failed: {e}")
raise
def get_deployment(self, deployment_id: str) -> Dict[str, Any]:
"""Fetches deployment configuration."""
url = f"{self.base_url}/api/v2/webmessaging/deployments/{deployment_id}"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
logger.info(f"Fetching deployment: {deployment_id}")
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if response.status_code == 404:
logger.error(f"Deployment {deployment_id} not found.")
elif response.status_code == 403:
logger.error("Forbidden. Check OAuth scopes.")
raise
def update_deployment(self, deployment_id: str, config: Dict[str, Any]) -> Dict[str, Any]:
"""Updates deployment configuration."""
url = f"{self.base_url}/api/v2/webmessaging/deployments/{deployment_id}"
headers = {
"Authorization": f"Bearer {self.access_token}",
"Content-Type": "application/json"
}
logger.info(f"Updating deployment: {deployment_id}")
try:
response = requests.put(url, json=config, headers=headers, timeout=10)
response.raise_for_status()
logger.info("Deployment updated successfully.")
return response.json()
except requests.exceptions.HTTPError as e:
logger.error(f"Update failed: {response.text}")
raise
def configure_launcher(self, deployment_id: str, color: str, position: str) -> None:
"""
High-level method to configure launcher color and position.
"""
# 1. Fetch
config = self.get_deployment(deployment_id)
# 2. Modify
if "uiConfig" not in config:
config["uiConfig"] = {}
if "launcher" not in config["uiConfig"]:
config["uiConfig"]["launcher"] = {}
logger.info(f"Setting launcher color to {color} and position to {position}")
config["uiConfig"]["launcher"]["color"] = color
config["uiConfig"]["launcher"]["position"] = position
# 3. Update
self.update_deployment(deployment_id, config)
def main():
# Configuration
REGION = os.getenv("GENESYS_CLOUD_REGION", "mypurecloud.com")
CLIENT_ID = os.getenv("GENESYS_CLOUD_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
DEPLOYMENT_ID = os.getenv("GENESYS_CLOUD_DEPLOYMENT_ID")
# Customization parameters
NEW_COLOR = "#00D4AA" # Teal
NEW_POSITION = "bottom-left"
if not all([CLIENT_ID, CLIENT_SECRET, DEPLOYMENT_ID]):
logger.error("Missing environment variables. Set GENESYS_CLOUD_CLIENT_ID, GENESYS_CLOUD_CLIENT_SECRET, and GENESYS_CLOUD_DEPLOYMENT_ID.")
sys.exit(1)
try:
# Initialize
configurer = WebMessagingConfigurer(REGION, CLIENT_ID, CLIENT_SECRET)
# Authenticate
configurer.authenticate()
# Configure
configurer.configure_launcher(DEPLOYMENT_ID, NEW_COLOR, NEW_POSITION)
logger.info("Process completed successfully.")
except Exception as e:
logger.error(f"An error occurred: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
Cause: The OAuth client does not have the required scopes.
Fix: Ensure the client has webmessaging:deployment:read and webmessaging:deployment:write. If you recently added scopes, generate a new token, as scopes are evaluated at token issuance time.
Error: 400 Bad Request - Invalid Position
Cause: The position field contains a value other than bottom-left, bottom-right, top-left, or top-right.
Fix: Validate the position string before sending the request. The API is strict about enum values.
Error: 429 Too Many Requests
Cause: You have exceeded the rate limit for the Web Messaging API.
Fix: Implement exponential backoff. The response headers Retry-After may indicate how long to wait.
import time
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
logger.warning(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
else:
raise
raise Exception("Max retries exceeded")
Error: UI Changes Not Visible Immediately
Cause: Browser caching or CDN propagation delay.
Fix: Clear your browser cache or open the web chat in an incognito window. Genesys Cloud caches the deployment configuration for the client-side widget. Changes may take up to 15 minutes to propagate globally, though usually, it is much faster.