Guest API: Sending structured quick replies via Open Messaging API

Is it possible to send structured messages (specifically quick replies) to a guest session using the Guest API (/api/v2/guest-sessions/{guestSessionId}/messages) via a ServiceNow integration?

I have a working webhook pipeline that exchanges OAuth tokens and maintains bi-directional sync for plain text updates. However, when I attempt to push a structured message to trigger a UI element in the guest widget, the API returns a 400 Bad Request.

Here is the payload I am sending in the POST body:

{
 "messageType": "application/vnd.nice.incontact.message.card",
 "payload": {
 "type": "quickReply",
 "content": "Select an option",
 "quickReplies": [
 {
 "text": "Option A",
 "value": "opt_a"
 }
 ]
 }
}

The response error is vague: Invalid message payload structure.

“Guest sessions support basic text messaging. Structured content such as cards or quick replies must be initiated by the agent or via the Web Messaging SDK client-side methods.”

This documentation snippet implies the API might be restricted. But I see messageType enums in the schema that suggest otherwise. Am I missing a specific header or is the payload structure incorrect for server-to-guest pushes? I need this for automated case status updates from ServiceNow.

3 Likes

TL;DR: The Guest API endpoint doesn’t support the full Open Messaging schema. You’ll need to use the standard Messaging API for structured payloads. I think this is a bit of a pain, honestly.

You need to shift away from the Guest API to the standard messages resource. The /api/v2/guest-sessions/{guestSessionId}/messages endpoint is restricted to basic text payloads for legacy widget compatibility and explicitly rejects complex Open Messaging structures, which is why you’re seeing that 400 error. To send quick replies, you have to post to the underlying conversation ID associated with that guest session.

Here’s a snippet using the CXone JavaScript SDK. I think this is the right approach, but I haven’t had a ton of time to test it:

const client = require('cxone-api-client');
// Assume auth is already handled via client.login(...)

async function sendQuickReplies(conversationId) {
 const body = {
 "messageType": "application/vnd.nice.incontact.message.card",
 "payload": {
 "type": "quickReply",
 "content": "Select an option:",
 "quickReplies": [
 {
 "text": "Option 1",
 "value": "opt_1"
 },
 {
 "text": "Option 2",
 "value": "opt_2"
 }
 ]
 }
 };

 try {
 const response = await client.conversations.postMessage(conversationId, body);
 console.log("Message posted:", response);
 } catch (error) {
 console.error("Failed to post message:", error);
 }
}

The key thing is the endpoint. The Guest API is a pretty thin wrapper. If you’re building a ServiceNow integration, you’ll need to map the guestSessionId to the conversationId first. I think you can do that by querying the GET /api/v2/conversations endpoint, filtering by a custom data field you’ve set on the guest session - you’ll need to check how you’re identifying guests in your setup. This should make sure the payload gets to the Open Messaging renderer in the widget. Double-check the SDK documentation for the exact structure of the body object; I think it’s pretty strict about the messageType and payload properties.

while the previous suggestion to use the Conversations API is valid, you can still achieve this via the Guest API if you strictly adhere to the text/plain content type and embed the quick reply metadata within the body as a specific JSON structure. the 400 error usually stems from sending application/json directly or malformed payload keys.

400 Bad Request: Invalid payload structure for guest message endpoint

here is a rust snippet using reqwest and serde_json to construct the correct payload. note the explicit content-type header.

use reqwest::Client;
use serde_json::json;

async fn send_quick_reply(guest_id: &str, token: &str) -> Result<(), Box<dyn std::error::Error>> {
 let client = Client::new();
 let payload = json!({
 "text": "Select an option:",
 "quickReplies": [
 { "text": "Option A", "value": "opt_a" },
 { "text": "Option B", "value": "opt_b" }
 ]
 });

 client.post(format!("https://api.mypurecloud.com/api/v2/knowledge/guest/sessions/{}/documents/answers", guest_id))
 .header("Authorization", format!("Bearer {}", token))
 .header("Content-Type", "text/plain")
 .body(payload.to_string())
 .send()
 .await?;

 Ok(())
}

ensure your service now integration handles the text/plain body correctly. the api parses the stringified json internally. this avoids the schema validation errors seen with application/json.

1 Like

The main issue here is that the Guest API endpoint is fundamentally limited to basic text payloads and explicitly rejects the complex Open Messaging schema required for quick replies. While the documentation suggests the API might support structured messages, it’s designed for simple text interactions.

If you try to send the structured payload directly via /api/v2/guest-sessions/{guestSessionId}/messages, you will hit that 400 error because the endpoint doesn’t parse complex JSON for interactive components. Instead, you need to use the broader Open Messaging API and establish a proper conversation context. Here’s the general approach:

First, ensure your guest session is associated with an active conversation. The Guest API is a bridge, and rich UI interaction requires the full power of Open Messaging. You’ll need to use the Conversations API to send a quick reply after the guest session has been migrated to a conversation.

While I can’t provide the exact endpoint due to the complexity of conversation routing, you’ll need to use the Open Messaging API’s message creation functionality, targeting the guest participant ID within the conversation. The payload structure should resemble this, but will require adaptation to your specific conversation setup:

{
 "to": [ { "id": "<guestParticipantId>" } ],
 "type": "message",
 "content": {
 "contentType": "application/vnd.nice.incontact.message+json",
 "content": {
 "type": "message",
 "text": "Select an option:",
 "quickReplies": [
 { "text": "Option 1", "value": "opt_1" },
 { "text": "Option 2", "value": "opt_2" }
 ]
 }
 }
}

Warning: Ensure your OAuth token has the necessary scopes for Open Messaging. If you’re using the CXone Web Messaging SDK, explore the available methods for sending structured messages within a conversation context rather than attempting to force it through the Guest API. The Guest API is best suited for simple text updates; for any rich UI interaction, the Open Messaging API is the reliable path. This should avoid the schema validation errors you’re seeing.