How to Access Participant Attributes Set by Web Messaging Inside an Architect Inbound Message Flow

How to Access Participant Attributes Set by Web Messaging Inside an Architect Inbound Message Flow

What You Will Build

  • You will build a Python script that queries the Genesys Cloud API to retrieve inbound message transcripts, specifically extracting custom participant attributes injected via the Web Messaging SDK.
  • This tutorial uses the Genesys Cloud PureCloudPlatformClientV2 Python SDK and the /api/v2/analytics/conversations/details/query endpoint.
  • The programming language covered is Python 3.9+.

Prerequisites

  • OAuth Client Type: A Genesys Cloud OAuth Client ID and Secret with the api grant type.
  • Required Scopes: analytics:conversation:read, webchat:read, messages:read.
  • SDK Version: genesys-cloud-purecloud-platform-client version 130.0.0 or higher.
  • Language/Runtime: Python 3.9+ with venv support.
  • External Dependencies:
    • genesys-cloud-purecloud-platform-client
    • python-dotenv (for secure credential management)

Authentication Setup

Genesys Cloud uses OAuth 2.0 for API authentication. For server-to-server integrations like this script, you will use the Client Credentials flow. The SDK handles the token retrieval and refresh automatically, but you must provide the correct credentials and environment.

Create a .env file in your project root with the following content:

GENESYS_CLOUD_REGION="mypurecloud.com"
GENESYS_CLOUD_CLIENT_ID="your_client_id_here"
GENESYS_CLOUD_CLIENT_SECRET="your_client_secret_here"

Install the required packages:

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

Initialize the SDK client in your Python script. This object will be used for all subsequent API calls.

import os
from dotenv import load_dotenv
from purecloud_platform_client import Configuration, ApiClient, PureCloudAuth

# Load environment variables
load_dotenv()

def get_authenticated_client():
    """
    Initializes and returns an authenticated PureCloud Platform Client.
    """
    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 client_id or not client_secret:
        raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set in .env")

    # Configure the OAuth client
    auth_config = PureCloudAuth(client_id, client_secret, region)
    
    # Create the API client with the auth configuration
    api_client = ApiClient(configuration=Configuration(oauth2_client=auth_config))
    
    return api_client

if __name__ == "__main__":
    try:
        client = get_authenticated_client()
        print("Authentication successful.")
    except Exception as e:
        print(f"Authentication failed: {e}")

Implementation

Step 1: Query Conversations with Participant Attributes

The core challenge in accessing participant attributes is that they are not always returned in the standard conversation summary. You must explicitly request them using the analytics/conversations/details/query endpoint. This endpoint allows you to define a filter for conversations and specify which details to return.

To access attributes set by the Web Messaging SDK (e.g., via genesys.cloud.webchat.setParticipantAttribute), you need to query for webchat type conversations and ensure the response includes participant details.

from purecloud_platform_client.rest import ApiException
from purecloud_platform_client.models import ConversationQueryRequest, ConversationQueryResult

def query_recent_webchat_conversations(api_client):
    """
    Queries the last 10 Webchat conversations to find those with participant attributes.
    """
    analytics_api = api_client.analytics_api

    # Define the query parameters
    # We filter by type 'webchat' and order by start time descending
    body = ConversationQueryRequest(
        size=10,
        filter=ConversationQueryRequest.Filter(
            conversation_type=["webchat"],
            date_range_type="start"
        ),
        order_by="start",
        order="desc",
        # Critical: Request participant details to see attributes
        view="full" 
    )

    try:
        # Execute the query
        response = analytics_api.post_analytics_conversations_details_query(body=body)
        return response
    except ApiException as e:
        print(f"Exception when calling AnalyticsApi->post_analytics_conversations_details_query: {e}")
        raise

if __name__ == "__main__":
    client = get_authenticated_client()
    result = query_recent_webchat_conversations(client)
    
    if result and result.conversations:
        print(f"Found {len(result.conversations)} conversations.")
    else:
        print("No conversations found.")

Step 2: Extracting Custom Participant Attributes

The response object contains a list of conversations. Each conversation has a participants array. Within each participant object, you will find an attributes dictionary. This is where custom attributes set via the Web Messaging SDK are stored.

Attributes set via the SDK are typically prefixed with custom: or are simply key-value pairs depending on how they were set. The Web Messaging SDK allows setting attributes that are persisted to the participant profile.

def extract_participant_attributes(conversation):
    """
    Extracts and prints custom attributes for all participants in a conversation.
    """
    print(f"\n--- Conversation ID: {conversation.id} ---")
    
    if not conversation.participants:
        print("No participants found.")
        return

    for participant in conversation.participants:
        # Identify if this is the customer (typically not associated with a user ID in webchat)
        is_customer = participant.user is None or participant.user.id is None
        
        participant_type = "Customer" if is_customer else f"Agent ({participant.user.name})"
        print(f"Participant: {participant_type} (ID: {participant.id})")

        if participant.attributes:
            # Filter for custom attributes
            # Genesys Cloud often prefixes custom attributes, but SDK-set ones might be raw keys
            custom_attrs = {k: v for k, v in participant.attributes.items() if not k.startswith("system:")}
            
            if custom_attrs:
                print(f"  Custom Attributes: {custom_attrs}")
            else:
                print("  No custom attributes found.")
        else:
            print("  No attributes object found.")

Step 3: Processing Results and Handling Pagination

The post_analytics_conversations_details_query endpoint supports pagination. If you need to process more than 10 conversations, you must use the next_page token provided in the response.

Additionally, you should handle cases where the conversation is still active or if the attributes are not yet propagated. Analytics data can have a slight delay (typically < 5 seconds) after the attribute is set.

def process_all_conversations(api_client, max_pages=3):
    """
    Processes conversations with pagination support.
    """
    page_count = 0
    next_page_token = None

    while page_count < max_pages:
        # Construct the query
        body = ConversationQueryRequest(
            size=10,
            filter=ConversationQueryRequest.Filter(
                conversation_type=["webchat"],
                date_range_type="start"
            ),
            order_by="start",
            order="desc",
            view="full"
        )

        # Add pagination token if available
        if next_page_token:
            body.page_token = next_page_token

        try:
            response = api_client.analytics_api.post_analytics_conversations_details_query(body=body)
            
            if not response.conversations:
                print("No more conversations to process.")
                break

            for conversation in response.conversations:
                extract_participant_attributes(conversation)

            # Check for next page
            next_page_token = response.next_page
            page_count += 1

            if not next_page_token:
                print("End of results.")
                break

        except ApiException as e:
            print(f"API Error on page {page_count}: {e}")
            break

if __name__ == "__main__":
    client = get_authenticated_client()
    process_all_conversations(client)

Complete Working Example

The following script combines authentication, querying, and attribute extraction into a single runnable module.

import os
import sys
from dotenv import load_dotenv
from purecloud_platform_client import Configuration, ApiClient, PureCloudAuth
from purecloud_platform_client.rest import ApiException
from purecloud_platform_client.models import ConversationQueryRequest

# Load environment variables
load_dotenv()

def get_authenticated_client():
    """
    Initializes and returns an authenticated PureCloud Platform Client.
    """
    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 client_id or not client_secret:
        raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set in .env")

    auth_config = PureCloudAuth(client_id, client_secret, region)
    api_client = ApiClient(configuration=Configuration(oauth2_client=auth_config))
    return api_client

def extract_participant_attributes(conversation):
    """
    Extracts and prints custom attributes for all participants in a conversation.
    """
    print(f"\n--- Conversation ID: {conversation.id} ---")
    
    if not conversation.participants:
        print("No participants found.")
        return

    for participant in conversation.participants:
        is_customer = participant.user is None or participant.user.id is None
        participant_type = "Customer" if is_customer else f"Agent ({participant.user.name})"
        print(f"Participant: {participant_type} (ID: {participant.id})")

        if participant.attributes:
            # Filter out system attributes if desired, or inspect all
            custom_attrs = {k: v for k, v in participant.attributes.items() if not k.startswith("system:")}
            
            if custom_attrs:
                print(f"  Custom Attributes: {custom_attrs}")
            else:
                print("  No custom attributes found.")
        else:
            print("  No attributes object found.")

def process_conversations(api_client, max_pages=2):
    """
    Queries and processes webchat conversations.
    """
    page_count = 0
    next_page_token = None

    while page_count < max_pages:
        body = ConversationQueryRequest(
            size=10,
            filter=ConversationQueryRequest.Filter(
                conversation_type=["webchat"],
                date_range_type="start"
            ),
            order_by="start",
            order="desc",
            view="full"
        )

        if next_page_token:
            body.page_token = next_page_token

        try:
            response = api_client.analytics_api.post_analytics_conversations_details_query(body=body)
            
            if not response.conversations:
                print("No more conversations to process.")
                break

            for conversation in response.conversations:
                extract_participant_attributes(conversation)

            next_page_token = response.next_page
            page_count += 1

            if not next_page_token:
                print("End of results.")
                break

        except ApiException as e:
            print(f"API Error on page {page_count}: {e}")
            break

if __name__ == "__main__":
    try:
        client = get_authenticated_client()
        print("Authentication successful. Fetching conversations...")
        process_conversations(client)
    except Exception as e:
        print(f"Fatal error: {e}")
        sys.exit(1)

Common Errors & Debugging

Error: 401 Unauthorized

  • Cause: The OAuth token is expired, invalid, or the client credentials are incorrect.
  • Fix: Verify your GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET in the .env file. Ensure the region is correct. The SDK handles token refresh, so this usually indicates a credential mismatch.

Error: 403 Forbidden

  • Cause: The OAuth client lacks the necessary scopes.
  • Fix: In the Genesys Cloud Admin portal, navigate to Admin > Security > OAuth Clients. Edit your client and ensure the following scopes are added:
    • analytics:conversation:read
    • webchat:read
    • messages:read
  • Restart your script after updating scopes.

Error: Participant Attributes Not Populated

  • Cause: The attributes were set in the Web Messaging SDK but have not yet propagated to the analytics database, or the query view is not full.
  • Fix:
    1. Ensure view="full" is set in the ConversationQueryRequest.
    2. Wait a few seconds after setting the attribute in the UI before querying.
    3. Verify the attribute key name. If you used genesys.cloud.webchat.setParticipantAttribute('myKey', 'myValue'), the key in the API response will be myKey.

Error: 429 Too Many Requests

  • Cause: You have exceeded the API rate limit.
  • Fix: Implement exponential backoff. The Python SDK does not handle retries automatically for all methods, so you may need to wrap the API call in a retry loop.
import time

def query_with_retry(api_client, body, max_retries=3):
    for attempt in range(max_retries):
        try:
            return api_client.analytics_api.post_analytics_conversations_details_query(body=body)
        except ApiException as e:
            if e.status == 429:
                wait_time = (2 ** attempt) + 1
                print(f"Rate limited. Retrying in {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                raise
    raise Exception("Max retries exceeded for 429 error")

Official References