Sending Structured Messages (Quick Replies, Cards) via the Open Messaging API

Sending Structured Messages (Quick Replies, Cards) via the Open Messaging API

What You Will Build

You will build a Python script that sends interactive rich media messages, including quick reply buttons and carousel cards, to a user via the Genesys Cloud Open Messaging API. This tutorial uses the Genesys Cloud Python SDK (genesyscloud) to construct and transmit complex MessagingEvent payloads. The code covers authentication, message construction, and error handling for 4xx and 5xx responses.

Prerequisites

  • OAuth Client Type: A Genesys Cloud OAuth client with the messaging:send scope.
  • SDK Version: genesyscloud SDK version 10.0.0 or later.
  • Runtime: Python 3.8+.
  • Dependencies:
    pip install genesyscloud
    
  • Environment Variables:
    • GENESYS_CLOUD_REGION: e.g., us-east-1
    • GENESYS_CLOUD_CLIENT_ID: Your OAuth client ID.
    • GENESYS_CLOUD_CLIENT_SECRET: Your OAuth client secret.
    • GENESYS_CLOUD_ORGANIZATION_ID: Your organization ID.

Authentication Setup

The Genesys Cloud API requires OAuth 2.0 Client Credentials grant flow. The SDK handles token acquisition and refresh automatically if configured correctly. You must initialize the PlatformClient with the correct region and credentials.

import os
from purecloudplatformclientv2 import PlatformClient
from purecloudplatformclientv2.api import MessagingApi

def get_messaging_api() -> MessagingApi:
    """
    Initializes and returns an authenticated MessagingApi instance.
    """
    # 1. Initialize the Platform Client
    platform = PlatformClient()

    # 2. Configure OAuth
    client_id = os.environ.get("GENESYS_CLOUD_CLIENT_ID")
    client_secret = os.environ.get("GENESYS_CLOUD_CLIENT_SECRET")
    region = os.environ.get("GENESYS_CLOUD_REGION", "us-east-1")

    if not client_id or not client_secret:
        raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set.")

    # Configure the OAuth client
    platform.oauth_client.set_client_id(client_id)
    platform.oauth_client.set_client_secret(client_secret)
    platform.oauth_client.set_region(region)

    # 3. Return the Messaging API client
    return MessagingApi(platform.api_client)

Scope Requirement: The OAuth client must have the messaging:send scope. Without this scope, the API will return a 403 Forbidden error.

Implementation

Step 1: Constructing a Quick Reply Message

Quick replies allow users to respond with a single tap. In the Open Messaging API, these are represented as Button objects within a Template or directly as Message components. We will construct a simple message with two quick reply options.

The core object is MessagingEvent. For sending a message, the type must be send. The content object contains the actual message payload.

from purecloudplatformclientv2.models import (
    MessagingEvent,
    Message,
    Button,
    Template,
    ExternalConversationReference
)

def build_quick_reply_event(user_id: str, channel_id: str) -> MessagingEvent:
    """
    Builds a MessagingEvent containing a text message with quick reply buttons.
    """
    # 1. Define the buttons
    button_yes = Button(
        type="button",
        text="Yes",
        value="yes_confirmed"
    )
    button_no = Button(
        type="button",
        text="No",
        value="no_declined"
    )

    # 2. Define the main message text
    text_message = Message(
        type="text",
        text="Do you want to schedule a follow-up call?"
    )

    # 3. Construct the template
    # Note: Some channels require templates to be pre-approved. 
    # For raw API usage, we often structure this as a composite message.
    template = Template(
        type="template",
        template_type="quick_reply",
        content=[text_message],
        buttons=[button_yes, button_no]
    )

    # 4. Define the external conversation reference
    # This links the message to a specific user on a specific channel
    ext_ref = ExternalConversationReference(
        id=user_id,
        channel_id=channel_id,
        provider_id="messenger" # Example provider
    )

    # 5. Construct the final event
    event = MessagingEvent(
        type="send",
        content=template,
        external_conversation_reference=ext_ref
    )

    return event

Key Parameters:

  • type: Must be send for outgoing messages.
  • external_conversation_reference: Identifies the recipient. The id is typically the user’s ID on the provider platform (e.g., Facebook Page Scoped ID), and channel_id is the Genesys Cloud channel ID.
  • buttons: A list of Button objects. The value field is what Genesys Cloud receives when the user clicks the button.

Step 2: Constructing a Carousel Card Message

Carousel cards allow you to send multiple cards (images, titles, descriptions, buttons) that the user can scroll through horizontally. This is supported on channels like Facebook Messenger and Webchat.

from purecloudplatformclientv2.models import (
    Card,
    Image,
    Button,
    Template,
    Message,
    ExternalConversationReference,
    MessagingEvent
)

def build_carousel_event(user_id: str, channel_id: str) -> MessagingEvent:
    """
    Builds a MessagingEvent containing a carousel of product cards.
    """
    # 1. Create the first card
    card_1_image = Image(
        url="https://example.com/images/product1.jpg"
    )
    card_1_button = Button(
        type="button",
        text="View Product",
        url="https://example.com/product1"
    )
    card_1 = Card(
        type="card",
        title="Product One",
        subtitle="High-quality widget",
        image=card_1_image,
        buttons=[card_1_button]
    )

    # 2. Create the second card
    card_2_image = Image(
        url="https://example.com/images/product2.jpg"
    )
    card_2_button = Button(
        type="button",
        text="View Product",
        url="https://example.com/product2"
    )
    card_2 = Card(
        type="card",
        title="Product Two",
        subtitle="Durable gadget",
        image=card_2_image,
        buttons=[card_2_button]
    )

    # 3. Construct the template
    template = Template(
        type="template",
        template_type="carousel",
        cards=[card_1, card_2]
    )

    # 4. Define the external conversation reference
    ext_ref = ExternalConversationReference(
        id=user_id,
        channel_id=channel_id,
        provider_id="messenger"
    )

    # 5. Construct the final event
    event = MessagingEvent(
        type="send",
        content=template,
        external_conversation_reference=ext_ref
    )

    return event

Key Parameters:

  • template_type: Set to carousel.
  • cards: A list of Card objects. Each card can have an image, title, subtitle, and buttons.
  • Button.type: For URLs, use url buttons. For postbacks, use button with a value.

Step 3: Sending the Message via API

Now that we have the MessagingEvent object, we send it using the post_messaging_messaging_events method. This method accepts a list of events, allowing for batch sending.

def send_message(api: MessagingApi, event: MessagingEvent) -> dict:
    """
    Sends a single messaging event and returns the response.
    Handles common HTTP errors.
    """
    try:
        # The API expects a list of events
        response = api.post_messaging_messaging_events(body=[event])
        
        # Check for success
        if response and response.body and len(response.body) > 0:
            result = response.body[0]
            print(f"Message sent successfully. Event ID: {result.id}")
            return result
        else:
            print("No response body returned.")
            return None

    except Exception as e:
        # Handle API exceptions
        print(f"Error sending message: {e}")
        raise

Complete Working Example

This script combines authentication, message construction, and sending. It demonstrates sending a quick reply message.

import os
from purecloudplatformclientv2 import PlatformClient
from purecloudplatformclientv2.api import MessagingApi
from purecloudplatformclientv2.models import (
    MessagingEvent,
    Message,
    Button,
    Template,
    ExternalConversationReference
)

def get_messaging_api() -> MessagingApi:
    """
    Initializes and returns an authenticated MessagingApi instance.
    """
    platform = PlatformClient()
    client_id = os.environ.get("GENESYS_CLOUD_CLIENT_ID")
    client_secret = os.environ.get("GENESYS_CLOUD_CLIENT_SECRET")
    region = os.environ.get("GENESYS_CLOUD_REGION", "us-east-1")

    if not client_id or not client_secret:
        raise ValueError("GENESYS_CLOUD_CLIENT_ID and GENESYS_CLOUD_CLIENT_SECRET must be set.")

    platform.oauth_client.set_client_id(client_id)
    platform.oauth_client.set_client_secret(client_secret)
    platform.oauth_client.set_region(region)

    return MessagingApi(platform.api_client)

def build_quick_reply_event(user_id: str, channel_id: str) -> MessagingEvent:
    """
    Builds a MessagingEvent containing a text message with quick reply buttons.
    """
    button_yes = Button(
        type="button",
        text="Yes",
        value="yes_confirmed"
    )
    button_no = Button(
        type="button",
        text="No",
        value="no_declined"
    )

    text_message = Message(
        type="text",
        text="Do you want to schedule a follow-up call?"
    )

    template = Template(
        type="template",
        template_type="quick_reply",
        content=[text_message],
        buttons=[button_yes, button_no]
    )

    ext_ref = ExternalConversationReference(
        id=user_id,
        channel_id=channel_id,
        provider_id="messenger"
    )

    event = MessagingEvent(
        type="send",
        content=template,
        external_conversation_reference=ext_ref
    )

    return event

def main():
    # 1. Initialize API
    api = get_messaging_api()

    # 2. Define recipient
    user_id = "123456789012345"  # Replace with actual user ID
    channel_id = "987654321098765" # Replace with actual channel ID

    # 3. Build message
    event = build_quick_reply_event(user_id, channel_id)

    # 4. Send message
    try:
        response = api.post_messaging_messaging_events(body=[event])
        if response.body:
            for item in response.body:
                if item.id:
                    print(f"Success: Event ID {item.id} sent.")
                elif item.error:
                    print(f"Error: {item.error.code} - {item.error.message}")
        else:
            print("No response received.")
    except Exception as e:
        print(f"Failed to send message: {e}")

if __name__ == "__main__":
    main()

Common Errors & Debugging

Error: 403 Forbidden

Cause: The OAuth client does not have the messaging:send scope.

Fix:

  1. Log in to the Genesys Cloud Admin portal.
  2. Navigate to Admin > Security > OAuth Clients.
  3. Select your client.
  4. Under Scopes, add messaging:send.
  5. Save the client.
  6. Regenerate the token or restart your application to acquire a new token with the updated scope.

Error: 400 Bad Request - Invalid Template

Cause: The template_type or structure does not match the channel’s requirements. For example, Facebook Messenger requires pre-approved templates for certain types, or the button count exceeds the limit (e.g., max 3 buttons for quick replies on some channels).

Fix:

  1. Check the template_type value. Ensure it matches the supported types for your provider (quick_reply, carousel, generic).
  2. Verify button limits. Facebook Messenger allows up to 3 quick reply buttons.
  3. Ensure image URLs are valid and accessible.

Error: 429 Too Many Requests

Cause: You have exceeded the rate limit for the messaging API.

Fix:
Implement exponential backoff in your sending logic.

import time

def send_with_retry(api: MessagingApi, event: MessagingEvent, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = api.post_messaging_messaging_events(body=[event])
            return response
        except Exception as e:
            if "429" in str(e) and attempt < max_retries - 1:
                wait_time = 2 ** attempt
                print(f"Rate limited. Waiting {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                raise

Error: 404 Not Found - Conversation Not Found

Cause: The external_conversation_reference points to a user or channel that does not exist or is not connected in Genesys Cloud.

Fix:

  1. Verify the user_id is correct for the provider.
  2. Ensure the channel_id is active and configured in Genesys Cloud.
  3. Check that the user has initiated a conversation with the bot/channel if required by the provider (e.g., Facebook Messenger 24-hour window).

Official References