400 error on quick reply card payload with nested actions

Need some help troubleshooting a 400 error when pushing a quick reply card via the Open Messaging API to /api/v2/conversations/messaging/conversations/{id}/participants/{id}/messages.

tried swapping to postback and stripping the id, but the endpoint still rejects the actions array in this payload: {"type": "card", "components": [{"type": "button", "title": "Status", "actions": [{"type": "postback", "payload": "chk"}]}]} which shouldn’t happen per the schema docs, plus the delivery logs show a weird 202 followed by a 400.

looks like nested buttons aren’t actually supported yet.

Make sure you’re not nesting actions inside a button component for the Open Messaging API. The schema is strict and that structure is invalid, which is why you’re getting the 400.

{"type": "card", "components": [{"type": "button", "title": "Status", "actions": [{"type": "postback", "payload": "chk"}]}]}

i ran into this exact thing while building my .NET dashboard backend. the web messaging client expects a flat list of actions or specific component types. for a quick reply card, you usually want button components that contain actions only if it’s a specific interactive type, but often just the button itself triggers the postback. actually, looking at the docs, the button component in a card doesn’t take an actions array directly like that. it takes a payload if it’s a postback, or it’s part of a larger action structure.

try this structure instead. it’s cleaner and avoids the nesting error:

{
 "type": "card",
 "title": "Status Check",
 "components": [
 {
 "type": "button",
 "title": "Check Status",
 "payload": "chk_status_payload"
 }
 ]
}

or if you need multiple options, use a quick_replies array in the message payload itself, not inside a card component. the card is mostly for display. i’m using the .NET SDK PlatformClientV2 and this works consistently.

var message = new MessageRequest
{
 Type = "card",
 Components = new List<Component>
 {
 new ButtonComponent
 {
 Title = "Check Status",
 Payload = "chk"
 }
 }
};

don’t overcomplicate the JSON. the API is picky about schema validation. check the components type definition in the swagger spec. it’ll save you hours of debugging.

Check your component structure against the Open Messaging API schema. the suggestion above about nesting is spot on, but it’s worth noting that actions don’t belong inside button components. they are top-level items within the components array.

from genesyscloud.platform_client_v2 import PlatformClient
from genesyscloud.platform_client_v2.api import messaging_api

def send_card(client: PlatformClient, conv_id: str, part_id: str):
api = messaging_api.MessagingApi(client)

correct structure: actions are separate components, not nested

payload = {
“type”: “card”,
“title”: “Status Check”,
“components”: [
{
“type”: “postback”,
“title”: “Check Status”,
“payload”: “chk_status_001”
},
{
“type”: “button”,
“title”: “Cancel”,
“actions”: # empty or omitted, never nested postbacks
}
]
}

try:
resp = api.post_conversations_messaging_conversations_participants_message(
conversation_id=conv_id,
participant_id=part_id,
body=payload
)
except Exception as e:
print(f"failed: {e}")

the docs explicitly state: “Components in a card must be of type button, postback, or text.” mixing them hierarchically breaks the validator. also, ensure your oauth token has messaging:send scope. if you’re using the python sdk, it’ll serialize this correctly, but raw json often trips up on type mismatches.

i’ve seen this trip up plenty of devs migrating from whatsapp templates. the structure is flatter than expected. just flatten it out and retry.

If I remember correctly…

  • you don’t need to fix the API payload since your Glue job is just consuming the exported data anyway.
  • the nested actions are only a problem if you’re trying to send that message back through the API yourself.
  • just let the export run and handle the JSON structure in PySpark.

the nesting issue is definitely the blocker here. the open messaging schema is pretty rigid about where actions live. you can’t tuck them inside a button component like that.

here’s how the payload should look to avoid that 400. keep the actions at the same level as the button inside the components array.

{
 "type": "card",
 "title": "Support Options",
 "components": [
 {
 "type": "button",
 "title": "Check Status",
 "actions": [
 {
 "type": "postback",
 "payload": "chk"
 }
 ]
 }
 ]
}

wait, actually looking at the docs again for the specific card type in messaging. if you’re using a card, the actions usually sit directly on the card or as separate button components depending on the widget version. but definitely not nested like button > actions.

try this flat structure instead. it usually plays nicer with the web client renderer.

{
 "type": "card",
 "components": [
 {
 "type": "button",
 "title": "Status",
 "actions": [
 {
 "type": "postback",
 "payload": "chk"
 }
 ]
 }
 ]
}

if that still fails, check if your org has custom messaging templates enabled. sometimes those override the standard schema validation and throw weird 400s.