Sending Proactive Notifications to a Customer with a Previous Web Messaging Session

Sending Proactive Notifications to a Customer with a Previous Web Messaging Session

What You Will Build

  • A Python script that identifies a customer by their email address or phone number from historical web messaging conversations.
  • A mechanism to initiate a proactive outbound web message to that customer using the Genesys Cloud Conversations API.
  • The integration of the Genesys Cloud Python SDK for authentication and conversation management.

Prerequisites

  • OAuth Client: A Genesys Cloud OAuth client configured with the public grant type or client_credentials grant type. For this tutorial, we assume a client_credentials flow for server-side automation.
  • Required Scopes:
    • analytics:query:read (to search historical conversations)
    • conversation:webchat:initiate (to start a proactive web chat)
    • user:login:read (optional, if resolving user IDs)
  • SDK Version: genesys-cloud-purecloud-platform-client version 170.0.0 or higher.
  • Language/Runtime: Python 3.9+
  • External Dependencies:
    • genesys-cloud-purecloud-platform-client
    • python-dotenv (for managing secrets)

Install the dependencies:

pip install genesys-cloud-purecloud-platform-client python-dotenv

Authentication Setup

Genesys Cloud uses OAuth 2.0 for API authentication. For server-to-server integrations, the client_credentials grant is the standard approach. The Genesys Cloud Python SDK handles token acquisition and refresh automatically when configured correctly.

Create a .env file in your project root:

GENESYS_CLOUD_REGION=us-east-1
GENESYS_CLOUD_CLIENT_ID=your_client_id
GENESYS_CLOUD_CLIENT_SECRET=your_client_secret

Initialize the SDK client:

import os
from dotenv import load_dotenv
from purecloud_platform_client import Configuration, ApiClient, AnalyticsApi, ConversationsApi
from purecloud_platform_client.rest import ApiException

load_dotenv()

def get_authenticated_client() -> ApiClient:
    """
    Creates and configures the Genesys Cloud API client.
    """
    config = Configuration(
        client_id=os.getenv("GENESYS_CLOUD_CLIENT_ID"),
        client_secret=os.getenv("GENESYS_CLOUD_CLIENT_SECRET"),
        region=os.getenv("GENESYS_CLOUD_REGION", "us-east-1")
    )
    api_client = ApiClient(configuration=config)
    return api_client

Implementation

Step 1: Identify the Customer via Historical Analytics

Before sending a proactive message, you must confirm the customer exists in your system and retrieve their previous interaction details. The Analytics API provides a robust query interface for this. We will search for conversations where the participant identity matches a known email address.

Endpoint: POST /api/v2/analytics/conversations/details/query
Scope: analytics:query:read

from purecloud_platform_client.models import (
    ConversationDetailsQueryRequest,
    ConversationDetailsQueryFilter,
    ConversationDetailsQueryFilterEntity,
    ConversationDetailsQueryFilterField
)

def find_customer_conversation(api_client: ApiClient, customer_email: str) -> dict | None:
    """
    Searches for the most recent web messaging conversation for a given email.
    """
    analytics_api = AnalyticsApi(api_client)
    
    # Define the filter for the participant email
    email_filter = ConversationDetailsQueryFilter(
        field=ConversationDetailsQueryFilterField("participants.email"),
        operator="eq",
        value=customer_email
    )
    
    # Define the entity type filter to limit to Web Messaging
    entity_filter = ConversationDetailsQueryFilterEntity(
        field="type",
        operator="eq",
        value="webchat"
    )
    
    # Construct the request
    query_request = ConversationDetailsQueryRequest(
        filters=[email_filter, entity_filter],
        size=1,  # We only need the most recent one
        sort_by=[{"field": "created_time", "order": "desc"}],
        interval="PT1H", # Required for analytics queries, even if not strictly used in this context
        from_time="2023-01-01T00:00:00Z", # Adjust as needed for your data retention policy
        to_time="2025-12-31T23:59:59Z"
    )

    try:
        response = analytics_api.post_analytics_conversations_details_query(body=query_request)
        
        if response.entities and len(response.entities) > 0:
            conversation = response.entities[0]
            return {
                "conversation_id": conversation.conversation_id,
                "participant_id": conversation.participants[0].id if conversation.participants else None,
                "email": customer_email
            }
        return None
    except ApiException as e:
        print(f"Analytics API Error: {e.status} {e.reason}")
        if e.body:
            print(e.body)
        return None

Step 2: Initiate the Proactive Web Messaging Session

Once the customer is identified, you can initiate a proactive web chat. This requires using the ConversationsApi to create a new conversation with the webchat type. The critical parameter is the initiation_mode set to proactive and providing the customer’s identity information in the participants array.

Endpoint: POST /api/v2/conversations
Scope: conversation:webchat:initiate

from purecloud_platform_client.models import (
    ConversationCreateRequest,
    ConversationParticipantRequest,
    ParticipantIdentity
)

def initiate_proactive_webchat(api_client: ApiClient, customer_email: str, routing_skill: str = None) -> str | None:
    """
    Initiates a proactive web messaging session for the specified customer.
    """
    conversations_api = ConversationsApi(api_client)
    
    # Define the participant identity
    participant_identity = ParticipantIdentity(
        email=customer_email
    )
    
    # Create the participant request
    participant_request = ConversationParticipantRequest(
        identity=participant_identity,
        type="customer"
    )
    
    # Construct the conversation creation request
    # Note: For proactive webchat, you typically do not specify a queue here directly 
    # if you want it to route based on skills or other criteria, but you can specify a queue if needed.
    # However, for pure proactive outreach, the system often requires a target queue or skill.
    
    conversation_request = ConversationCreateRequest(
        type="webchat",
        initiation_mode="proactive",
        participants=[participant_request]
    )
    
    # Optional: Add routing metadata if required by your flow
    if routing_skill:
        # This is a simplified representation. Real routing might require more complex objects
        # depending on whether you are targeting a specific queue or skill group.
        pass

    try:
        response = conversations_api.post_conversations(body=conversation_request)
        print(f"Proactive conversation initiated: {response.conversation_id}")
        return response.conversation_id
    except ApiException as e:
        print(f"Conversations API Error: {e.status} {e.reason}")
        if e.body:
            print(e.body)
        return None

Step 3: Send the Initial Message (Optional but Recommended)

Initiating the conversation creates the session, but it does not automatically send a message. To engage the customer, you must send a message on behalf of the system or an agent. Since this is a proactive notification, you might want to send an automated welcome message.

Endpoint: POST /api/v2/conversations/{conversationId}/messages
Scope: conversation:webchat:message

from purecloud_platform_client.models import (
    ConversationMessageCreateRequest,
    MessageBody
)

def send_initial_message(api_client: ApiClient, conversation_id: str, message_text: str) -> bool:
    """
    Sends the first message in the proactive conversation.
    """
    conversations_api = ConversationsApi(api_client)
    
    message_body = MessageBody(
        text=message_text
    )
    
    # The sender is typically the system or an agent. 
    # For proactive, we often use a system-level identity or a specific agent if assigned.
    # If no agent is assigned yet, sending as a system message might be limited.
    # A common pattern is to assign an agent first, or send as a 'system' if allowed.
    # Here we assume a basic text message.
    
    message_request = ConversationMessageCreateRequest(
        body=message_body,
        type="text"
    )

    try:
        # Note: The participant ID of the sender is often required. 
        # If sending as a system, this might be handled differently.
        # For simplicity, we assume the conversation has a participant context.
        conversations_api.post_conversations_messages(
            conversation_id=conversation_id,
            body=message_request
        )
        return True
    except ApiException as e:
        print(f"Message Send Error: {e.status} {e.reason}")
        return False

Complete Working Example

This script combines the steps into a single executable module. It loads credentials, finds a customer, initiates a proactive chat, and sends a message.

import os
import sys
from dotenv import load_dotenv
from purecloud_platform_client import ApiClient, Configuration, AnalyticsApi, ConversationsApi
from purecloud_platform_client.rest import ApiException
from purecloud_platform_client.models import (
    ConversationDetailsQueryRequest,
    ConversationDetailsQueryFilter,
    ConversationDetailsQueryFilterEntity,
    ConversationDetailsQueryFilterField,
    ConversationCreateRequest,
    ConversationParticipantRequest,
    ParticipantIdentity,
    ConversationMessageCreateRequest,
    MessageBody
)

def load_config():
    load_dotenv()
    config = Configuration(
        client_id=os.getenv("GENESYS_CLOUD_CLIENT_ID"),
        client_secret=os.getenv("GENESYS_CLOUD_CLIENT_SECRET"),
        region=os.getenv("GENESYS_CLOUD_REGION", "us-east-1")
    )
    return ApiClient(configuration=config)

def find_customer(api_client: ApiClient, email: str):
    analytics_api = AnalyticsApi(api_client)
    email_filter = ConversationDetailsQueryFilter(
        field=ConversationDetailsQueryFilterField("participants.email"),
        operator="eq",
        value=email
    )
    entity_filter = ConversationDetailsQueryFilterEntity(
        field="type",
        operator="eq",
        value="webchat"
    )
    query = ConversationDetailsQueryRequest(
        filters=[email_filter, entity_filter],
        size=1,
        sort_by=[{"field": "created_time", "order": "desc"}],
        interval="PT1H",
        from_time="2020-01-01T00:00:00Z",
        to_time="2025-12-31T23:59:59Z"
    )
    try:
        resp = analytics_api.post_anversations_details_query(body=query)
        if resp.entities:
            return resp.entities[0]
        return None
    except ApiException as e:
        print(f"Error finding customer: {e}")
        return None

def main():
    customer_email = "customer@example.com"
    welcome_message = "Hello! We noticed you visited our site. How can we help?"
    
    api_client = load_config()
    
    # Step 1: Find Customer
    print(f"Searching for customer: {customer_email}")
    customer_data = find_customer(api_client, customer_email)
    
    if not customer_data:
        print("Customer not found in historical web chats. Skipping.")
        return

    print(f"Customer found. Last conversation ID: {customer_data.conversation_id}")

    # Step 2: Initiate Proactive Chat
    print("Initiating proactive web chat...")
    conversations_api = ConversationsApi(api_client)
    
    identity = ParticipantIdentity(email=customer_email)
    participant = ConversationParticipantRequest(identity=identity, type="customer")
    req = ConversationCreateRequest(
        type="webchat",
        initiation_mode="proactive",
        participants=[participant]
    )
    
    try:
        new_conversation = conversations_api.post_conversations(body=req)
        conv_id = new_conversation.conversation_id
        print(f"Proactive conversation started: {conv_id}")
        
        # Step 3: Send Message
        print(f"Sending message: {welcome_message}")
        msg_body = MessageBody(text=welcome_message)
        msg_req = ConversationMessageCreateRequest(body=msg_body, type="text")
        
        # Note: Sending a message in a proactive chat often requires an agent to be assigned 
        # or using a specific system identity. This may fail if no routing has occurred.
        # For a true proactive notification, you might use the Notification API instead 
        # if Web Chat is not active on the client side.
        conversations_api.post_conversations_messages(conversation_id=conv_id, body=msg_req)
        print("Message sent successfully.")
        
    except ApiException as e:
        print(f"Failed to initiate or message: {e.status} {e.reason}")
        if e.body:
            print(e.body)

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 403 Forbidden

  • Cause: The OAuth token lacks the required scope.
  • Fix: Ensure conversation:webchat:initiate and analytics:query:read are added to your OAuth client’s scopes in the Genesys Cloud Admin console.
  • Code Check: Verify Configuration initialization includes the correct client ID and secret.

Error: 400 Bad Request - “Initiation mode proactive requires a target queue or skill”

  • Cause: Proactive web chats often require a routing context to determine where the conversation should go if the customer responds.
  • Fix: Add a routing object to the ConversationCreateRequest specifying a queue ID or skill group ID.
  • Code Fix:
    from purecloud_platform_client.models import ConversationRoutingMetadata
    # ...
    routing_meta = ConversationRoutingMetadata(
        queue_id="your_queue_id"
    )
    conversation_request.routing = routing_meta
    

Error: 409 Conflict - “Customer already has an active session”

  • Cause: The customer currently has an open web chat session in their browser.
  • Fix: Check the status of existing conversations for this customer. Only initiate proactive chats if no active sessions exist.
  • Debugging: Use the find_customer function to check the status field of the returned conversation entity. If status is active, do not initiate a new one.

Error: 429 Too Many Requests

  • Cause: Hitting API rate limits.
  • Fix: Implement exponential backoff in your API calls. The Genesys Cloud Python SDK does not automatically retry 429s, so you must handle this in your wrapper code.
  • Code Fix: Use a library like tenacity or write a simple retry loop.

Official References