Could someone explain why my Guzzle request to POST /api/v2/conversations/webchat/messages fails with a 400 Bad Request when trying to send a canned response? I have verified the OAuth token is valid. I am passing the conversationId and messageId correctly. My JSON payload looks like this: { “content”: { “type”: “text”, “text”: “{"type":"canned","cannedId":"abc-123"}” } }. The API docs mention text must be a string, but I suspect the nested JSON encoding for the canned reference is triggering the validation error. How should I structure the body?
Ah, yeah, this is a known issue with nested JSON encoding in HTTP requests where the text field expects a raw string, not a serialized object representation.
The problem lies in how you are constructing the content payload. The Genesys Cloud API expects the text property to contain the actual string content that will be displayed. For canned responses, you do not send a JSON object {"type":"canned","cannedId":"abc-123"} as the text value. Instead, you must structure the request body to explicitly define the content type as canned and provide the cannedId directly within the content object, rather than nesting it inside a text string. This avoids the double-encoding error and ensures the platform correctly interprets the intent.
import requests
import json
headers = {
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
}
# Correct payload structure for canned response
payload = {
"content": {
"type": "canned",
"cannedId": "abc-123"
}
}
response = requests.post(
f'https://api.us.genesyscloud.com/api/v2/conversations/webchat/messages',
headers=headers,
json=payload
)
print(response.status_code)
print(response.text)
Ensure your OAuth token includes the conversation:webchat:send scope, and verify that the cannedId exists in the same division as the conversation context to avoid permission errors.
As far as I remember, the JS SDK serializes the text field as a plain string, so you are double-encoding the JSON object.
const payload: MessageCreateRequest = {
content: {
type: 'text',
text: '{"type":"canned","cannedId":"abc-123"}' // Raw string, not an object
}
};
Ensure your HTTP client does not stringify this field again before sending.
I’d recommend looking at at the text field serialization. The API rejects the payload because the text property contains a JSON string representation instead of the actual message content derived from the canned response.
Retrieve the canned text via GET /api/v2/external/cannedresponses/{id} and pass that raw string.
{
"content": {
"type": "text",
"text": "Actual resolved text from canned response"
}
}
if i remember correctly, you cannot send canned response refs via the messages api. it expects raw text. fetch the canned content first using get /api/v2/external/cannedresponses/{id} and send that string in the text field.