Send a message
curl --request POST \
--url http://localhost:8080/chat/sessions/{session_id}/send \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": {
"type": "text",
"text": "<string>",
"attachments": [
{
"name": "<string>",
"url": "<string>",
"base64": "<string>",
"mime_type": "<string>"
}
]
},
"contact": {
"name": "<string>",
"email": "[email protected]",
"phone_number": "<string>",
"avatar_url": "<string>",
"custom_data": {}
},
"custom_data": {},
"agent": {
"id": 123,
"name": "<string>",
"avatar_url": "<string>"
},
"should_take_over_session_from_ai": false
}
'import requests
url = "http://localhost:8080/chat/sessions/{session_id}/send"
payload = {
"message": {
"type": "text",
"text": "<string>",
"attachments": [
{
"name": "<string>",
"url": "<string>",
"base64": "<string>",
"mime_type": "<string>"
}
]
},
"contact": {
"name": "<string>",
"email": "[email protected]",
"phone_number": "<string>",
"avatar_url": "<string>",
"custom_data": {}
},
"custom_data": {},
"agent": {
"id": 123,
"name": "<string>",
"avatar_url": "<string>"
},
"should_take_over_session_from_ai": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
message: {
type: 'text',
text: '<string>',
attachments: [{name: '<string>', url: '<string>', base64: '<string>', mime_type: '<string>'}]
},
contact: {
name: '<string>',
email: '[email protected]',
phone_number: '<string>',
avatar_url: '<string>',
custom_data: {}
},
custom_data: {},
agent: {id: 123, name: '<string>', avatar_url: '<string>'},
should_take_over_session_from_ai: false
})
};
fetch('http://localhost:8080/chat/sessions/{session_id}/send', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/chat/sessions/{session_id}/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'message' => [
'type' => 'text',
'text' => '<string>',
'attachments' => [
[
'name' => '<string>',
'url' => '<string>',
'base64' => '<string>',
'mime_type' => '<string>'
]
]
],
'contact' => [
'name' => '<string>',
'email' => '[email protected]',
'phone_number' => '<string>',
'avatar_url' => '<string>',
'custom_data' => [
]
],
'custom_data' => [
],
'agent' => [
'id' => 123,
'name' => '<string>',
'avatar_url' => '<string>'
],
'should_take_over_session_from_ai' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://localhost:8080/chat/sessions/{session_id}/send"
payload := strings.NewReader("{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:8080/chat/sessions/{session_id}/send")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/chat/sessions/{session_id}/send")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"message": {
"id": "<string>"
}
}
}{
"statusCode": 123,
"message": "<string>",
"error": "<string>"
}Sessions
Send a message
Send a message — Sends a message in a chat session. Use when orchestrating conversations from code — creating sessions, sending messages, or listing history for an external…
POST
/
chat
/
sessions
/
{session_id}
/
send
Send a message
curl --request POST \
--url http://localhost:8080/chat/sessions/{session_id}/send \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": {
"type": "text",
"text": "<string>",
"attachments": [
{
"name": "<string>",
"url": "<string>",
"base64": "<string>",
"mime_type": "<string>"
}
]
},
"contact": {
"name": "<string>",
"email": "[email protected]",
"phone_number": "<string>",
"avatar_url": "<string>",
"custom_data": {}
},
"custom_data": {},
"agent": {
"id": 123,
"name": "<string>",
"avatar_url": "<string>"
},
"should_take_over_session_from_ai": false
}
'import requests
url = "http://localhost:8080/chat/sessions/{session_id}/send"
payload = {
"message": {
"type": "text",
"text": "<string>",
"attachments": [
{
"name": "<string>",
"url": "<string>",
"base64": "<string>",
"mime_type": "<string>"
}
]
},
"contact": {
"name": "<string>",
"email": "[email protected]",
"phone_number": "<string>",
"avatar_url": "<string>",
"custom_data": {}
},
"custom_data": {},
"agent": {
"id": 123,
"name": "<string>",
"avatar_url": "<string>"
},
"should_take_over_session_from_ai": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
message: {
type: 'text',
text: '<string>',
attachments: [{name: '<string>', url: '<string>', base64: '<string>', mime_type: '<string>'}]
},
contact: {
name: '<string>',
email: '[email protected]',
phone_number: '<string>',
avatar_url: '<string>',
custom_data: {}
},
custom_data: {},
agent: {id: 123, name: '<string>', avatar_url: '<string>'},
should_take_over_session_from_ai: false
})
};
fetch('http://localhost:8080/chat/sessions/{session_id}/send', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/chat/sessions/{session_id}/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'message' => [
'type' => 'text',
'text' => '<string>',
'attachments' => [
[
'name' => '<string>',
'url' => '<string>',
'base64' => '<string>',
'mime_type' => '<string>'
]
]
],
'contact' => [
'name' => '<string>',
'email' => '[email protected]',
'phone_number' => '<string>',
'avatar_url' => '<string>',
'custom_data' => [
]
],
'custom_data' => [
],
'agent' => [
'id' => 123,
'name' => '<string>',
'avatar_url' => '<string>'
],
'should_take_over_session_from_ai' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://localhost:8080/chat/sessions/{session_id}/send"
payload := strings.NewReader("{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://localhost:8080/chat/sessions/{session_id}/send")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/chat/sessions/{session_id}/send")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": {\n \"type\": \"text\",\n \"text\": \"<string>\",\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"mime_type\": \"<string>\"\n }\n ]\n },\n \"contact\": {\n \"name\": \"<string>\",\n \"email\": \"[email protected]\",\n \"phone_number\": \"<string>\",\n \"avatar_url\": \"<string>\",\n \"custom_data\": {}\n },\n \"custom_data\": {},\n \"agent\": {\n \"id\": 123,\n \"name\": \"<string>\",\n \"avatar_url\": \"<string>\"\n },\n \"should_take_over_session_from_ai\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"message": {
"id": "<string>"
}
}
}{
"statusCode": 123,
"message": "<string>",
"error": "<string>"
}AI email replies
For enabled Zendesk email sessions using a connected inbox, an AI response can includedata.autopilotResponse.emailDelivered: true. OpenCX has already sent that email reply;
do not send another copy. This field is optional: its absence does not indicate delivery
failure, because other delivery routes keep their existing behavior.
Responses with assistMode: true remain drafts and are not emailed to the customer.
See Zendesk email recipients for recipient changes
and cancelled replies.
Custom data
This endpoint accepts two independentcustom_data objects, each merged into a different record:
- Top-level
custom_data— session-level attributes that belong to this conversation, such as an order ID, cart value, or routing flag. Merged into the chat session and returned by Get Session. Values can be strings, numbers, or booleans. contact.custom_data— contact-level attributes that belong to the person, such as plan, region, or account tier. Merged into the contact. Values are strings.
400.
{
"sender": "contact",
"custom_data": { "order_id": "ord_123", "cart_value": 149.99, "priority": "high" },
"contact": { "custom_data": { "plan": "pro", "region": "emea" } },
"message": { "type": "text", "text": "I need help with this order" }
}
Session
custom_data set through this endpoint is stored on the session but is not synced to a connected external ticketing system (e.g. Intercom or HubSpot).Option taps
An agent reply sent from a macro with options carries them on theconversation.agent_response webhook as options, next to the reply’s message_id, and on the message’s event.options in session history. On API sessions your client renders them. When the customer taps one, send a contact message that names the tap in message.option_reply. The message is recorded with the option’s label as its text, and an option that isn’t on a message of this session is rejected with a 400:
{
"sender": "contact",
"message": {
"type": "text",
"text": "Yes",
"option_reply": {
"message_id": "7c3f1a7e-2a51-4b8e-9d0f-6f1b2c3d4e5f",
"option_id": "b1e2c3d4-0000-4a5b-8c9d-112233445566"
}
}
}
workflow option runs the macro’s Macro Option Clicked workflows and the AI doesn’t reply; a text option is handled like any contact message. A tap that doesn’t count, and the same text sent without option_reply, is handled as a normal message. option_reply is rejected with a 400 when sender is agent.Authorizations
Body
application/json
Was this page helpful?