Sending Proactive Web Chat Notifications to Returning Customers

Sending Proactive Web Chat Notifications to Returning Customers

What You Will Build

  • You will build a service that identifies a returning customer from a previous web chat session and sends them a proactive notification to re-engage them.
  • This uses the Genesys Cloud CX API, specifically the Conversations API for retrieving interaction history and the Messaging API for sending proactive messages.
  • The tutorial covers implementation in Python using the genesyscloud SDK.

Prerequisites

  • OAuth Client: You need a Genesys Cloud OAuth client with the Web Messaging capability enabled.
  • Required Scopes:
    • conversation:read (to retrieve previous conversation details and user identities).
    • webchat:write (to send proactive messages).
    • webchat:read (to check current session status).
  • SDK Version: genesyscloud Python SDK v1.0.0 or later.
  • Language/Runtime: Python 3.8+.
  • External Dependencies:
    • genesyscloud
    • requests (for fallback HTTP calls if needed, though SDK is preferred).
    • python-dotenv (for managing credentials securely).

Authentication Setup

Genesys Cloud uses OAuth 2.0 for API authentication. For server-to-server integrations like this one, you will use the Client Credentials flow. The SDK handles token management automatically, but understanding the underlying flow is critical for debugging 401 errors.

Install the SDK and required libraries:

pip install genesyscloud python-dotenv

Create a .env file with your credentials:

GENESYS_REGION=us-east-1
GENESYS_CLIENT_ID=your_client_id_here
GENESYS_CLIENT_SECRET=your_client_secret_here

Initialize the SDK in your Python script. The PureCloudPlatformClientV2 class manages the OAuth token lifecycle, including automatic refresh when the token expires.

import os
from dotenv import load_dotenv
from purecloud_platform_client_v2 import Configuration, ApiClient, ConversationsApi, WebChatApi

# Load environment variables
load_dotenv()

def get_api_client():
    """
    Initializes and returns a configured Genesys Cloud API client.
    """
    # Configure the API client
    configuration = Configuration(
        host=f"https://api.{os.getenv('GENESYS_REGION')}.mypurecloud.com",
        client_id=os.getenv('GENESYS_CLIENT_ID'),
        client_secret=os.getenv('GENESYS_CLIENT_SECRET')
    )

    # Create the API client
    api_client = ApiClient(configuration)
    return api_client

# Initialize clients
api_client = get_api_client()
conversations_api = ConversationsApi(api_client)
webchat_api = WebChatApi(api_client)

Implementation

Step 1: Identify the Returning Customer

Before sending a proactive message, you must identify the customer. In a web chat context, customers are often identified by a unique identifier passed during the initial connection, such as an email address, user ID, or a custom parameter. Genesys Cloud stores this information in the person object of the conversation.

We will assume your application has a way to look up a customer by their email or user ID and retrieve the conversationId of their last session. If you do not have this stored, you can search for recent conversations using the Conversations API.

For this tutorial, we will assume you have the conversationId from a previous session. If you need to find it, you can use the get_conversations_webchat endpoint with filters, but that is beyond the scope of this specific proactive notification flow.

Here is how you retrieve the details of the previous conversation to extract the necessary identifiers for the proactive message:

from purecloud_platform_client_v2.rest import ApiException
from purecloud_platform_client_v2.models import ConversationWebchat

def get_previous_conversation_details(conversation_id: str) -> ConversationWebchat:
    """
    Retrieves details of a previous web chat conversation.
    This is used to confirm the conversation is closed and to extract participant details.
    """
    try:
        # Get the conversation details
        # Scope: conversation:read
        response = conversations_api.get_conversations_webchat(
            conversation_id=conversation_id,
            expand=["participants", "transcripts"]
        )
        
        # Verify the conversation is closed before attempting proactive engagement
        if response.status != "closed" and response.status != "abandoned":
            raise ValueError(f"Conversation {conversation_id} is still active with status: {response.status}")
            
        return response

    except ApiException as e:
        if e.status == 404:
            print(f"Conversation {conversation_id} not found.")
        elif e.status == 403:
            print(f"Access denied. Check if 'conversation:read' scope is included.")
        else:
            print(f"API Error: {e}")
        raise

Step 2: Construct the Proactive Message Payload

To send a proactive message, you must use the post_webchat_proactive endpoint. This endpoint requires a specific JSON body containing the conversationId of the new session (or null if starting a new one) and the message content.

However, a proactive message is not just a text blob. It is a web chat message that triggers a new conversation or reactivates an existing one. You must provide:

  1. conversationId: The ID of the new conversation that will be created. If you are re-engaging a user who has an open session, you use that ID. If they are not currently connected, you must first establish a connection or create a new conversation context.
  2. message: The content of the message.
  3. from: The participant ID of the agent or bot sending the message.

Critical Note: You cannot send a proactive message to a user who is not currently connected to a web chat session. Genesys Cloud Web Chat requires an active WebSocket connection. Therefore, “proactive” in this context usually means:

  • The user has re-opened the chat widget, and you want to send a message immediately upon connection.
  • You are using the Genesys Cloud Web Chat JavaScript SDK to trigger a proactive message on the client side when the user returns to the page.

For server-side initiation, you typically send a message to an active session. If the user is not active, you cannot push a message to their browser. This tutorial assumes the user has just re-established a web chat session (e.g., they clicked the chat widget), and you want to send a personalized greeting based on their history.

To achieve this, you need the conversationId of the current active session. The Web Chat client SDK usually provides this. If you are building a backend service that reacts to a webhook event (e.g., webchat:conversation:start), you will receive the conversationId in the webhook payload.

Let’s assume you have received a webhook indicating a new web chat session has started for a known user. You now have the new_conversation_id.

from purecloud_platform_client_v2.models import WebChatProactiveMessage

def create_proactive_message_payload(new_conversation_id: str, agent_id: str, message_text: str) -> dict:
    """
    Constructs the payload for a proactive web chat message.
    """
    payload = {
        "conversationId": new_conversation_id,
        "message": {
            "type": "text",
            "text": message_text
        },
        "from": {
            "id": agent_id,
            "type": "agent"
        }
    }
    return payload

Step 3: Send the Proactive Message

Now, use the post_webchat_proactive endpoint to send the message. This endpoint is specifically designed for server-initiated messages to a web chat session.

def send_proactive_message(new_conversation_id: str, agent_id: str, message_text: str) -> bool:
    """
    Sends a proactive message to an active web chat session.
    """
    try:
        # Construct the payload
        payload = create_proactive_message_payload(new_conversation_id, agent_id, message_text)
        
        # Send the proactive message
        # Scope: webchat:write
        webchat_api.post_webchat_proactive(
            body=payload
        )
        
        print(f"Proactive message sent successfully to conversation {new_conversation_id}")
        return True

    except ApiException as e:
        if e.status == 400:
            print(f"Bad Request: Invalid payload or conversation ID. {e.body}")
        elif e.status == 403:
            print(f"Access denied. Check if 'webchat:write' scope is included.")
        elif e.status == 404:
            print(f"Conversation {new_conversation_id} not found or not active.")
        elif e.status == 429:
            print("Rate limit exceeded. Implement retry logic.")
        else:
            print(f"API Error: {e.status} - {e.body}")
        return False

Complete Working Example

This complete script demonstrates how to handle a webhook event indicating a new web chat session for a returning customer. It retrieves the previous conversation history, constructs a personalized message, and sends it proactively.

import os
import json
from dotenv import load_dotenv
from purecloud_platform_client_v2 import Configuration, ApiClient, ConversationsApi, WebChatApi
from purecloud_platform_client_v2.rest import ApiException

# Load environment variables
load_dotenv()

class GenesysProactiveNotifier:
    def __init__(self):
        # Initialize API client
        configuration = Configuration(
            host=f"https://api.{os.getenv('GENESYS_REGION')}.mypurecloud.com",
            client_id=os.getenv('GENESYS_CLIENT_ID'),
            client_secret=os.getenv('GENESYS_CLIENT_SECRET')
        )
        self.api_client = ApiClient(configuration)
        self.conversations_api = ConversationsApi(self.api_client)
        self.webchat_api = WebChatApi(self.api_client)

    def get_customer_history(self, conversation_id: str) -> dict:
        """
        Retrieves previous conversation details for a customer.
        """
        try:
            response = self.conversations_api.get_conversations_webchat(
                conversation_id=conversation_id,
                expand=["participants"]
            )
            
            # Extract customer email or ID from participants
            customer_participant = None
            for participant in response.participants:
                if participant.type == "customer":
                    customer_participant = participant
                    break
            
            if not customer_participant:
                return {}

            return {
                "email": customer_participant.address,
                "previous_conversation_id": conversation_id
            }

        except ApiException as e:
            print(f"Error retrieving history: {e}")
            return {}

    def send_proactive_greeting(self, new_conversation_id: str, agent_id: str, customer_email: str):
        """
        Sends a proactive message to the new web chat session.
        """
        # Personalize the message
        message_text = f"Welcome back! We see you previously contacted us regarding your account. How can we help you today, {customer_email.split('@')[0]}?"
        
        payload = {
            "conversationId": new_conversation_id,
            "message": {
                "type": "text",
                "text": message_text
            },
            "from": {
                "id": agent_id,
                "type": "agent"
            }
        }

        try:
            self.webchat_api.post_webchat_proactive(body=payload)
            print(f"Proactive greeting sent to {new_conversation_id}")
        except ApiException as e:
            print(f"Failed to send proactive message: {e}")

# Example usage
if __name__ == "__main__":
    notifier = GenesysProactiveNotifier()
    
    # Simulate receiving a webhook with a new conversation ID for a returning customer
    # In a real scenario, this data would come from a webhook handler
    webhook_data = {
        "conversationId": "new-conversation-id-12345",
        "previousConversationId": "old-conversation-id-67890",
        "agentId": "agent-id-11111"
    }

    # Step 1: Get customer history from the previous conversation
    history = notifier.get_customer_history(webhook_data["previousConversationId"])
    
    if history and "email" in history:
        # Step 2: Send proactive message to the new conversation
        notifier.send_proactive_greeting(
            new_conversation_id=webhook_data["conversationId"],
            agent_id=webhook_data["agentId"],
            customer_email=history["email"]
        )
    else:
        print("No previous history found or error retrieving history.")

Common Errors & Debugging

Error: 403 Forbidden

  • Cause: The OAuth client used does not have the required scopes.
  • Fix: Ensure your OAuth client has webchat:write and conversation:read scopes assigned in the Genesys Cloud Admin Portal under Security > OAuth 2.0 Clients.

Error: 404 Not Found

  • Cause: The conversationId provided in the proactive message payload is invalid or the conversation is no longer active.
  • Fix: Verify that the conversationId belongs to an active web chat session. Proactive messages can only be sent to sessions that are currently open. If the user closed the widget, the session is terminated, and you cannot send a message.

Error: 429 Too Many Requests

  • Cause: You have exceeded the rate limit for the post_webchat_proactive endpoint.
  • Fix: Implement exponential backoff retry logic. The Retry-After header in the response indicates how many seconds to wait before retrying.
import time

def send_with_retry(webchat_api, payload, max_retries=3):
    for attempt in range(max_retries):
        try:
            webchat_api.post_webchat_proactive(body=payload)
            return True
        except ApiException as e:
            if e.status == 429:
                retry_after = int(e.headers.get('Retry-After', 2 ** attempt))
                print(f"Rate limited. Retrying in {retry_after} seconds...")
                time.sleep(retry_after)
            else:
                raise
    return False

Error: Message Not Received by User

  • Cause: The user’s browser does not have an active WebSocket connection to Genesys Cloud.
  • Fix: Proactive messages are pushed over WebSocket. If the user is not actively viewing the page with the chat widget, or if the widget is minimized and the connection is dropped, the message will not be delivered. Ensure the Web Chat SDK is properly initialized and maintains a connection.

Official References