Open Messaging API rejecting quick replies with 'Invalid message format' on web channel

Hey folks,

Running into a wall trying to send structured messages (specifically quick replies) via the Open Messaging API. I’m using the Python SDK to push a message to a web chat participant, but every time I try to include actions, the API throws a 400 Bad Request.

The error message is pretty generic:

{"message": "Invalid message format", "code": "bad_request", "status": 400}

Here’s the payload I’m constructing. I’ve double-checked the actions array structure against the docs, and it looks right to me. The text field is under 500 chars, and the action types are valid.

{
 "from": {
 "id": "system",
 "name": "Bot"
 },
 "to": {
 "id": "participant-id-123",
 "name": "Guest"
 },
 "type": "message",
 "text": "What would you like to do next?",
 "actions": [
 {
 "type": "quick-reply",
 "title": "Check Balance",
 "payload": "CHECK_BALANCE"
 },
 {
 "type": "quick-reply",
 "title": "Talk to Agent",
 "payload": "TRANSFER_AGENT"
 }
 ]
}

If I strip out the actions block, the plain text message goes through fine. So the auth and participant ID are correct. I’m calling POST /api/v2/conversations/messages.

I’ve tried swapping quick-reply for button just in case, but same result. Is there a specific capability flag I need to enable on the web channel settings for this to work? Or am I missing a field in the JSON?

The participant is active in a web chat conversation. I’ve verified this by sending a plain text message first.

The Open Messaging API is notoriously picky about the structure of actions inside the text object, especially when you’re dealing with web channels. It’s not just about putting the text in there; the schema validation expects a very specific hierarchy for quick replies. If you’re sending a flat array or missing the type field, the server rejects it before it even hits the messaging engine.

Here’s how I usually structure that payload using the Python SDK (genesyscloud). The key is ensuring the actions array is nested correctly within the text object and that each action has a valid type and text.

from genesyscloud import messaging_api
from genesyscloud.messaging_api.model.quick_reply_action import QuickReplyAction
from genesyscloud.messaging_api.model.text_content import TextContent
from genesyscloud.messaging_api.model.message import Message

def send_quick_reply(conversation_id, participant_id):
 # Define the quick reply actions
 action1 = QuickReplyAction(
 type="quickReply",
 text="Yes, I need help",
 value="yes_help"
 )
 action2 = QuickReplyAction(
 type="quickReply",
 text="No, thanks",
 value="no_thanks"
 )

 # Create the text content with actions
 text_obj = TextContent(
 text="How can I assist you today?",
 actions=[action1, action2]
 )

 # Construct the message
 msg = Message(
 text=text_obj
 )

 # Send via API
 try:
 api_instance = messaging_api.MessagingApi()
 api_instance.post_conversations_messaging_participant_messages(
 conversation_id=conversation_id,
 participant_id=participant_id,
 body=msg
 )
 except Exception as e:
 print(f"Failed to send message: {e}")

send_quick_reply("your_conversation_id", "your_participant_id")

Make sure you’re using the latest version of the SDK. Older versions had some quirks with how they serialized the actions array, sometimes dropping the type field entirely if it wasn’t explicitly set. Also, double-check that your value fields are strings. The API doesn’t like integers or nulls there. If you’re still getting the 400, enable debug logging on the SDK to see the raw JSON being sent. That usually reveals if there’s an extra nested object or a missing quote somewhere.