Sending Proactive Notifications to a Customer via Genesys Cloud Web Messaging
What You Will Build
- One sentence: This tutorial demonstrates how to programmatically initiate a web messaging session with a customer who previously engaged with your web widget, using their stored
contactId. - One sentence: This uses the Genesys Cloud CX Web Messaging API (
/api/v2/webchat/messaging) and the Python SDK. - One sentence: The programming language covered is Python 3.9+.
Prerequisites
- OAuth Client Type: Public or Confidential Client with the
webchat:messagingscope. - Required Scopes:
webchat:messaging,webchat:session:read(if you need to verify session status before sending). - SDK Version:
genesys-cloud-purecloud-platform-client>= 150.0.0. - Runtime: Python 3.9 or higher.
- Dependencies:
genesys-cloud-purecloud-platform-client,pyjwt(optional, for debugging tokens),httpx(for direct API calls if preferred, though SDK is recommended).
Install the SDK via pip:
pip install genesys-cloud-purecloud-platform-client
Authentication Setup
Genesys Cloud uses OAuth 2.0. For server-to-server communication (like sending proactive messages), you should use the Client Credentials Grant. This flow does not require user interaction and returns an access token with the scopes granted to your client application.
Below is a robust authentication helper that caches tokens and handles expiration automatically.
import time
import os
from typing import Optional
from purecloud_platform_client import Configuration, ApiClient
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.api_client: Optional[ApiClient] = None
self.token_expiry: float = 0
self.access_token: str = ""
def get_api_client(self) -> ApiClient:
"""
Returns a configured ApiClient. Handles token refresh if expired.
"""
if self.api_client is None or time.time() > self.token_expiry:
self._refresh_token()
return self.api_client
def _refresh_token(self) -> None:
"""
Fetches a new OAuth token using Client Credentials Grant.
"""
config = Configuration()
# Set the base path based on environment (e.g., us-east-1.mypurecloud.com)
config.host = f"https://{self.environment}"
# Get token
token_response = config.get_access_token(
client_id=self.client_id,
client_secret=self.client_secret,
scopes=["webchat:messaging"] # Only request needed scopes
)
self.access_token = token_response.access_token
# Set expiry slightly before actual expiry to avoid race conditions
self.token_expiry = time.time() + token_response.expires_in - 10
self.api_client = ApiClient(configuration=config)
Implementation
Step 1: Identify the Previous Customer Session
Before sending a proactive message, you must have a valid contactId or session_id from a previous interaction. In a typical architecture, this ID is stored in your CRM or database when the customer first opens the web widget.
If you do not have the ID stored, you cannot send a proactive message to a specific anonymous user. Web Messaging in Genesys Cloud is session-based. The contactId is the unique identifier for that specific browser session’s engagement with Genesys.
For this tutorial, we assume you have retrieved the contactId from your database. If you need to find it, you would query the webchat:session:read endpoint, but that requires the session_id which is tied to the browser. Therefore, proactive messaging relies entirely on your application storing the contactId during the initial connect or message event.
Step 2: Construct the Proactive Message Payload
To send a message, you use the POST /api/v2/webchat/messaging endpoint. The critical parameter here is contactId. You also need to specify the type of the message. For proactive notifications, the type is usually agent or bot, depending on whether you want it to appear as a human agent or an automated bot.
The request body requires:
contactId: The ID of the customer session.type: The sender type (agentorbot).message: The content of the message.
Important: If the session has been terminated (e.g., the customer closed the browser or the session timed out), sending a message will fail. You must handle this gracefully.
Here is the code to send the message using the SDK.
from purecloud_platform_client import WebMessagingApi
from purecloud_platform_client.models import WebMessageRequestBody
def send_proactive_message(
api_client: ApiClient,
contact_id: str,
message_text: str,
sender_type: str = "bot"
) -> dict:
"""
Sends a proactive message to a specific contact ID.
Args:
api_client: The authenticated ApiClient.
contact_id: The contact ID from the previous session.
message_text: The text content of the message.
sender_type: 'agent' or 'bot'. Default is 'bot'.
Returns:
dict: The response from the API.
"""
api_instance = WebMessagingApi(api_client)
# Construct the request body
body = WebMessageRequestBody(
contact_id=contact_id,
type=sender_type,
message=message_text
)
try:
# Execute the API call
# The API returns a 200 OK with the message details if successful
response = api_instance.post_webchat_messaging(body=body)
print(f"Message sent successfully. Message ID: {response.id}")
return response
# Note: In the Python SDK, the response object might be a WebMessage object
except Exception as e:
# Handle specific HTTP errors
if hasattr(e, 'status') and e.status == 400:
print(f"Bad Request: The contact ID might be invalid or the session is expired. Details: {e.body}")
elif hasattr(e, 'status') and e.status == 404:
print(f"Not Found: The contact ID does not exist in Genesys Cloud.")
elif hasattr(e, 'status') and e.status == 429:
print(f"Rate Limited: Too many requests. Wait before retrying.")
else:
print(f"Error sending message: {e}")
raise e
Step 3: Handling Session Expiration and Retries
Web messaging sessions in Genesys Cloud have a timeout (default is often 15-30 minutes of inactivity). If you attempt to send a proactive message to an expired session, the API will return a 400 Bad Request or 404 Not Found.
You should implement a retry mechanism with exponential backoff for transient errors (429, 5xx), but fail fast for 400/404 errors as they indicate a permanent state (invalid/expired contact).
import time
from purecloud_platform_client import ApiClient
def send_with_retry(
auth: GenesysAuth,
contact_id: str,
message_text: str,
max_retries: int = 3
) -> bool:
"""
Sends a message with retry logic for rate limits and server errors.
Fails immediately on 400/404.
"""
api_client = auth.get_api_client()
for attempt in range(max_retries):
try:
send_proactive_message(api_client, contact_id, message_text)
return True
except Exception as e:
# Check if it is a retryable error
is_retryable = False
if hasattr(e, 'status'):
status = e.status
if status == 429:
is_retryable = True
wait_time = 2 ** attempt # Exponential backoff
print(f"Rate limited. Retrying in {wait_time} seconds...")
elif 500 <= status < 600:
is_retryable = True
wait_time = 2 ** attempt
print(f"Server error {status}. Retrying in {wait_time} seconds...")
if is_retryable:
time.sleep(wait_time)
else:
# Non-retryable error (e.g., 400, 404)
print(f"Non-retryable error on attempt {attempt + 1}. Aborting.")
return False
print(f"Failed to send message after {max_retries} retries.")
return False
Complete Working Example
Below is a complete, runnable Python script. Replace the placeholder values with your Genesys Cloud credentials and a valid contactId.
import os
import time
from purecloud_platform_client import Configuration, ApiClient, WebMessagingApi
from purecloud_platform_client.models import WebMessageRequestBody
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.api_client = None
self.token_expiry = 0
def get_api_client(self) -> ApiClient:
if self.api_client is None or time.time() > self.token_expiry:
self._refresh_token()
return self.api_client
def _refresh_token(self) -> None:
config = Configuration()
config.host = f"https://{self.environment}"
token_response = config.get_access_token(
client_id=self.client_id,
client_secret=self.client_secret,
scopes=["webchat:messaging"]
)
self.token_expiry = time.time() + token_response.expires_in - 10
self.api_client = ApiClient(configuration=config)
def send_proactive_message(
api_client: ApiClient,
contact_id: str,
message_text: str,
sender_type: str = "bot"
):
api_instance = WebMessagingApi(api_client)
body = WebMessageRequestBody(
contact_id=contact_id,
type=sender_type,
message=message_text
)
try:
response = api_instance.post_webchat_messaging(body=body)
print(f"Success: Message sent with ID {response.id}")
return response
except Exception as e:
if hasattr(e, 'status'):
if e.status == 400:
raise ValueError(f"Invalid Contact ID or Session Expired: {e.body}")
elif e.status == 404:
raise ValueError(f"Contact ID not found: {e.body}")
elif e.status == 429:
raise ConnectionError("Rate limited. Please retry later.")
raise e
def main():
# Configuration
CLIENT_ID = os.getenv("GENESYS_CLIENT_ID", "YOUR_CLIENT_ID")
CLIENT_SECRET = os.getenv("GENESYS_CLIENT_SECRET", "YOUR_CLIENT_SECRET")
ENVIRONMENT = os.getenv("GENESYS_ENVIRONMENT", "us-east-1.mypurecloud.com")
CONTACT_ID = os.getenv("GENESYS_CONTACT_ID", "YOUR_PREVIOUS_CONTACT_ID")
MESSAGE = "Hello! We noticed you were looking at our summer collection. Do you have any questions?"
# Initialize Auth
auth = GenesysAuth(CLIENT_ID, CLIENT_SECRET, ENVIRONMENT)
# Get API Client
api_client = auth.get_api_client()
# Send Message
try:
send_proactive_message(
api_client=api_client,
contact_id=CONTACT_ID,
message_text=MESSAGE,
sender_type="bot"
)
except ValueError as ve:
print(f"Validation Error: {ve}")
except ConnectionError as ce:
print(f"Connection Error: {ce}")
except Exception as e:
print(f"Unexpected Error: {e}")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 400 Bad Request - “Contact ID is invalid or session has expired”
- What causes it: The
contactIdprovided does not correspond to an active web messaging session. Sessions expire after a period of inactivity (configurable in Genesys Cloud, default is often 15 minutes). Alternatively, thecontactIdformat is incorrect. - How to fix it:
- Verify the
contactIdwas captured from a recent session. - Check the session timeout settings in your Genesys Cloud Web Messaging configuration.
- If the session is expired, you cannot “wake it up.” You must treat this as a new session. You can prompt the user to start a new chat via your web widget instead.
- Verify the
- Code showing the fix:
try: send_proactive_message(...) except ValueError as e: if "expired" in str(e).lower(): # Logic to notify user to start new chat print("Session expired. Please start a new chat.") else: print(f"Error: {e}")
Error: 403 Forbidden - “Insufficient Permissions”
- What causes it: The OAuth token used does not have the
webchat:messagingscope. - How to fix it: Ensure your OAuth client in Genesys Cloud has the
webchat:messagingscope assigned. When requesting the token, ensure you specifyscopes=["webchat:messaging"]. - Code showing the fix:
# In _refresh_token token_response = config.get_access_token( client_id=self.client_id, client_secret=self.client_secret, scopes=["webchat:messaging"] # Ensure this scope is present )
Error: 429 Too Many Requests
- What causes it: You are exceeding the API rate limits for the Web Messaging endpoint.
- How to fix it: Implement exponential backoff and retry logic. Do not retry immediately.
- Code showing the fix: See the
send_with_retryfunction in Step 3.
Error: 404 Not Found
- What causes it: The
contactIddoes not exist in Genesys Cloud. - How to fix it: Verify the
contactIdwas stored correctly from the initial widget connection. If you are using a custom integration, ensure thecontactIdis passed correctly in theconnectormessageevents.