curl --request POST \
--url https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "<string>",
"display_content": "<string>",
"file_ids": [
"<string>"
],
"mentioned_file_ids": [
"<string>"
],
"model": "<string>",
"on_busy": "queue",
"idempotency_key": "<string>"
}
'import requests
url = "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages"
payload = {
"message": "<string>",
"display_content": "<string>",
"file_ids": ["<string>"],
"mentioned_file_ids": ["<string>"],
"model": "<string>",
"on_busy": "queue",
"idempotency_key": "<string>"
}
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: '<string>',
display_content: '<string>',
file_ids: ['<string>'],
mentioned_file_ids: ['<string>'],
model: '<string>',
on_busy: 'queue',
idempotency_key: '<string>'
})
};
fetch('https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages",
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' => '<string>',
'display_content' => '<string>',
'file_ids' => [
'<string>'
],
'mentioned_file_ids' => [
'<string>'
],
'model' => '<string>',
'on_busy' => 'queue',
'idempotency_key' => '<string>'
]),
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 := "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages"
payload := strings.NewReader("{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\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("https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}Create message and stream response
Send a follow-up message in an existing conversation and stream the agent’s reply as Server-Sent Events (same event shapes as conversation creation).
If the conversation is already working on a previous message, on_busy
decides what happens: queue (default) durably enqueues this message to run
next — the stream first emits a status event with message: "queued"
and its queuedMessageId/position, keeps the connection alive with
heartbeats, then streams the reply once the turn dispatches. reject returns
a conversation_busy 409 instead. Send a stable idempotency_key to make
a retry resume the same queued message. Manage waiting messages with the
/queue endpoints.
curl --request POST \
--url https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": "<string>",
"display_content": "<string>",
"file_ids": [
"<string>"
],
"mentioned_file_ids": [
"<string>"
],
"model": "<string>",
"on_busy": "queue",
"idempotency_key": "<string>"
}
'import requests
url = "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages"
payload = {
"message": "<string>",
"display_content": "<string>",
"file_ids": ["<string>"],
"mentioned_file_ids": ["<string>"],
"model": "<string>",
"on_busy": "queue",
"idempotency_key": "<string>"
}
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: '<string>',
display_content: '<string>',
file_ids: ['<string>'],
mentioned_file_ids: ['<string>'],
model: '<string>',
on_busy: 'queue',
idempotency_key: '<string>'
})
};
fetch('https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages",
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' => '<string>',
'display_content' => '<string>',
'file_ids' => [
'<string>'
],
'mentioned_file_ids' => [
'<string>'
],
'model' => '<string>',
'on_busy' => 'queue',
'idempotency_key' => '<string>'
]),
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 := "https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages"
payload := strings.NewReader("{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\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("https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/projects/{project_id}/conversations/{chat_id}/messages")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": \"<string>\",\n \"display_content\": \"<string>\",\n \"file_ids\": [\n \"<string>\"\n ],\n \"mentioned_file_ids\": [\n \"<string>\"\n ],\n \"model\": \"<string>\",\n \"on_busy\": \"queue\",\n \"idempotency_key\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"code": "<string>",
"request_id": "<string>"
}Authorizations
The access token received from the authorization server in the OAuth 2.0 flow.
Body
A follow-up message in an existing conversation.
on_busy and idempotency_key are reply-only: they matter when a
conversation may already be running a turn. Creating a conversation always
dispatches its first turn (a new chat is never busy), and chat creation is not
itself idempotency-keyed, so a fused create cannot honor a retry key — those
fields would be a false promise there and live on the reply request only.
User message to send to the Summation agent.
1Optional alternate rendering of the message for display; the agent still receives message.
Project file ids to attach to the message as context.
Project file ids referenced inline in the message.
Chat model id to use for this turn, from GET /v1/chat-models. Per-request model selection is honored where enabled for the tenant; otherwise the default model applies. The reply metadata reports the model that ran.
1Reasoning effort for this turn (low, medium, high, xhigh, max). Honored under the same tenant setting as model selection; otherwise the default effort applies.
low, medium, high, xhigh, max What to do when the conversation is already working on a previous message: 'queue' durably enqueues this turn to run next (streamed once it dispatches); 'reject' returns a conversation_busy error instead.
queue, reject Optional client-supplied key that makes this reply retry-safe: replaying the same key returns the same accepted turn instead of creating a duplicate. Supply a stable key per logical message when retrying.
1 - 128Response
Server-Sent Events stream of agent activity.
Each event is a UTF-8 line prefixed with event:, id:, or data: per the SSE spec. The data: payload is a JSON object with the shape {"type": <event_type>, "sequence": <int>, "data": <object>}.
Event types emitted by the agent:
status— high-level progress;data.message(queued/running text) ordata.summary(short status line). When the conversation was busy and the message was queued (request fieldon_busy: "queue"), the first event isstatuswithdata.message: "queued"(or"held"if a Stop has already paused the queue),data.queuedMessageId,data.position,data.behindMessageId,data.held, anddata.revision; the reply then streams once the queued turn dispatches. A held queue does not run until it is resumed.queue.updated— the waiting message's queue state changed;data.position,data.held,data.revision,data.queuedMessageId.held: truemeans a Stop paused the queue: the message will not run until the queue is resumed (POST .../queue/resume).revisiononly moves forward; ignore any snapshot older than one you hold.tool.started— agent invoked a tool;data.id,data.nametool.input— tool input arguments;data.id,data.name,data.inputtool.completed— tool finished;data.idmessage.delta— incremental assistant text;data.text(append in order)done— terminal event;data.messageId,data.content(final assistant message),data.status(complete/error/cancelled)error— terminal error;data.code,data.message. A queued message may end withcode: "queue_wait_timeout"after 30 minutes of waiting — the message is still queued and can be observed again via the queue endpoints.
Resumability: when the connection drops before done, the same stream can be resumed via GET /v1/projects/{project_id}/conversations/{chat_id}/messages/{message_id}/events. Before a queued message has bound an assistant message id, reconnect by re-sending with the same idempotency_key, or poll GET /v1/projects/{project_id}/conversations/{chat_id}/queue/{queued_message_id}.