curl -X POST https://api.trellis.sh/v1/chats \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "message": "Show me the top 5 projects by budget as a bar chart", "chat_id": "chat_abc123", "integration_id": "550e8400-e29b-41d4-a716-446655440000" }'
import requestsimport jsonresponse = requests.post( "https://api.trellis.sh/v1/chats", headers={ "Authorization": "Bearer YOUR_TOKEN", "Content-Type": "application/json", "Accept": "text/event-stream", }, json={ "message": "Show me the top 5 projects by budget as a bar chart", "chat_id": "chat_abc123", "integration_id": "550e8400-e29b-41d4-a716-446655440000", }, stream=True)current_event = Nonefor line in response.iter_lines(decode_unicode=True): if line.startswith("event: "): current_event = line[7:] elif line.startswith("data: ") and current_event: data = json.loads(line[6:]) if current_event == "visualization": if data["type"] == "chart": print(f"Chart ({data['chart_type']}): {data.get('title')}") print(f" {len(data['data']['values'])} data points") elif data["type"] == "table": print(f"Table: {data['headers']}") elif current_event == "message": print(data["content"]) current_event = None
const response = await fetch("https://api.trellis.sh/v1/chats", { method: "POST", headers: { Authorization: "Bearer YOUR_TOKEN", "Content-Type": "application/json", Accept: "text/event-stream", }, body: JSON.stringify({ message: "Show me the top 5 projects by budget as a bar chart", chat_id: "chat_abc123", integration_id: "550e8400-e29b-41d4-a716-446655440000", }),});const reader = response.body.getReader();const decoder = new TextDecoder();let buffer = "";let currentEvent = "";while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { if (line.startsWith("event: ")) { currentEvent = line.slice(7); } else if (line.startsWith("data: ") && currentEvent) { const data = JSON.parse(line.slice(6)); if (currentEvent === "visualization") { if (data.type === "chart") { renderChart(data.chart_type, data.title, data.data); } else if (data.type === "table") { renderTable(data.headers, data.rows); } } else if (currentEvent === "message") { displayMessage(data.content); } currentEvent = ""; } }}
event: chat_metadatadata: {"id": "chat_abc123", "user_message_id": "msg_001"}event: processingdata: {"status": "thinking"}event: processingdata: {"status": "analyzing"}event: text_deltadata: {"content": "There were 1,523 orders "}event: text_deltadata: {"content": "placed last month (December 2024)."}event: messagedata: {"content": "There were 1,523 orders placed last month (December 2024).", "id": "msg_xyz789"}
event: chat_metadatadata: {"id": "chat_def456", "user_message_id": "msg_001"}event: processingdata: {"status": "thinking"}event: processingdata: {"status": "analyzing"}event: visualizationdata: {"id": "ab12xy3456", "type": "chart", "chart_type": "bar", "title": "Top 5 Projects by Budget", "data": {"values": [{"label": "Marina Gate Phase 2", "value": 22100000}, {"label": "Green Spine Boulevard", "value": 31600000}, {"label": "Al Raha Beach Tower", "value": 12800000}, {"label": "Yas Mall Extension", "value": 8400000}, {"label": "Admin Building Retrofit", "value": 5200000}], "x_axis_label": "Project", "y_axis_label": "Budget (AED)"}}event: messagedata: {"content": "Here are the top 5 projects ranked by budget. Green Spine Boulevard leads at AED 31.6M, followed by Marina Gate Phase 2 at AED 22.1M.", "id": "msg_abc789"}
event: chat_metadatadata: {"id": "chat_ghi789", "user_message_id": "msg_001"}event: processingdata: {"status": "analyzing"}event: visualizationdata: {"id": "cd34ef5678", "type": "table", "headers": ["Project Name", "Status", "Budget", "Completion %"], "rows": [["Al Raha Beach Tower", "Construction", "12,800,000", "73.46"], ["Yas Mall Extension", "Design", "8,400,000", "15.00"], ["Admin Building Retrofit", "DLP and Project Closeout", "5,200,000", "100.00"]]}event: messagedata: {"content": "Here are the active projects with their current status and budgets.", "id": "msg_def012"}
{ "detail": { "code": "MISSING_CHAT_ID", "message": "Provide chat_id; create one via POST /v1/chats/create" }}
{ "detail": { "code": "CHAT_NOT_FOUND", "message": "chat_id does not exist" }}
Chats
Send Message (SSE)
Send a chat message and stream the response via SSE
Send a natural language message and receive a streamed response via Server-Sent Events (SSE).
For a complete guide on handling responses, see the Chat Overview.Prefer real-time bidirectional communication? Use the WebSocket endpoint instead.
chat_id is now required. Call Create Chat first to obtain one — sending without chat_id returns 400 MISSING_CHAT_ID, and an unknown ID returns 404 CHAT_NOT_FOUND.
ID of an existing chat. Obtain one from Create Chat. Omitting this field returns 400 MISSING_CHAT_ID; passing an ID that doesn’t belong to the caller returns 404 CHAT_NOT_FOUND.
ID of a saved query to execute deterministically. When provided, the saved query’s SQL is run directly instead of passing the message through the AI agent. Use List Saved Queries to retrieve query IDs.
Key-value pairs supplying runtime values for {{param_name}} placeholders in the saved query’s SQL. Required when the saved query has a non-empty parameters array. Only applicable when saved_query_id is set.
{ "year": "2025", "limit": "10" }
When saved_query_id is provided, the AI agent is bypassed and the saved query’s SQL is executed directly against the connected database. The message field is still required but is used only for display in the chat history. Any {{param_name}} placeholders in the SQL must have corresponding entries in parameters — missing values will return a 400 error.
An incremental chunk of the final assistant text, streamed as the model generates it. Multiple frames arrive per turn. Concatenate content values in order to build the response.
message
{"content": "...", "id": "msg_..."}
The final response. Always carries the authoritative full content plus the persisted message id. Safe to overwrite any streaming buffer with content on receipt.
An error occurred. See WebSocket → Error codes for the stable code enum and retry semantics.
text_delta events let you render the response progressively — typical time-to-first-token is about 1 second vs. several seconds for the full answer. See Chat Overview → Streaming text deltas for the buffer-and-reconcile pattern.
curl -X POST https://api.trellis.sh/v1/chats \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: text/event-stream" \ -d '{ "message": "Show me the top 5 projects by budget as a bar chart", "chat_id": "chat_abc123", "integration_id": "550e8400-e29b-41d4-a716-446655440000" }'
import requestsimport jsonresponse = requests.post( "https://api.trellis.sh/v1/chats", headers={ "Authorization": "Bearer YOUR_TOKEN", "Content-Type": "application/json", "Accept": "text/event-stream", }, json={ "message": "Show me the top 5 projects by budget as a bar chart", "chat_id": "chat_abc123", "integration_id": "550e8400-e29b-41d4-a716-446655440000", }, stream=True)current_event = Nonefor line in response.iter_lines(decode_unicode=True): if line.startswith("event: "): current_event = line[7:] elif line.startswith("data: ") and current_event: data = json.loads(line[6:]) if current_event == "visualization": if data["type"] == "chart": print(f"Chart ({data['chart_type']}): {data.get('title')}") print(f" {len(data['data']['values'])} data points") elif data["type"] == "table": print(f"Table: {data['headers']}") elif current_event == "message": print(data["content"]) current_event = None
const response = await fetch("https://api.trellis.sh/v1/chats", { method: "POST", headers: { Authorization: "Bearer YOUR_TOKEN", "Content-Type": "application/json", Accept: "text/event-stream", }, body: JSON.stringify({ message: "Show me the top 5 projects by budget as a bar chart", chat_id: "chat_abc123", integration_id: "550e8400-e29b-41d4-a716-446655440000", }),});const reader = response.body.getReader();const decoder = new TextDecoder();let buffer = "";let currentEvent = "";while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { if (line.startsWith("event: ")) { currentEvent = line.slice(7); } else if (line.startsWith("data: ") && currentEvent) { const data = JSON.parse(line.slice(6)); if (currentEvent === "visualization") { if (data.type === "chart") { renderChart(data.chart_type, data.title, data.data); } else if (data.type === "table") { renderTable(data.headers, data.rows); } } else if (currentEvent === "message") { displayMessage(data.content); } currentEvent = ""; } }}
event: chat_metadatadata: {"id": "chat_abc123", "user_message_id": "msg_001"}event: processingdata: {"status": "thinking"}event: processingdata: {"status": "analyzing"}event: text_deltadata: {"content": "There were 1,523 orders "}event: text_deltadata: {"content": "placed last month (December 2024)."}event: messagedata: {"content": "There were 1,523 orders placed last month (December 2024).", "id": "msg_xyz789"}
event: chat_metadatadata: {"id": "chat_def456", "user_message_id": "msg_001"}event: processingdata: {"status": "thinking"}event: processingdata: {"status": "analyzing"}event: visualizationdata: {"id": "ab12xy3456", "type": "chart", "chart_type": "bar", "title": "Top 5 Projects by Budget", "data": {"values": [{"label": "Marina Gate Phase 2", "value": 22100000}, {"label": "Green Spine Boulevard", "value": 31600000}, {"label": "Al Raha Beach Tower", "value": 12800000}, {"label": "Yas Mall Extension", "value": 8400000}, {"label": "Admin Building Retrofit", "value": 5200000}], "x_axis_label": "Project", "y_axis_label": "Budget (AED)"}}event: messagedata: {"content": "Here are the top 5 projects ranked by budget. Green Spine Boulevard leads at AED 31.6M, followed by Marina Gate Phase 2 at AED 22.1M.", "id": "msg_abc789"}
event: chat_metadatadata: {"id": "chat_ghi789", "user_message_id": "msg_001"}event: processingdata: {"status": "analyzing"}event: visualizationdata: {"id": "cd34ef5678", "type": "table", "headers": ["Project Name", "Status", "Budget", "Completion %"], "rows": [["Al Raha Beach Tower", "Construction", "12,800,000", "73.46"], ["Yas Mall Extension", "Design", "8,400,000", "15.00"], ["Admin Building Retrofit", "DLP and Project Closeout", "5,200,000", "100.00"]]}event: messagedata: {"content": "Here are the active projects with their current status and budgets.", "id": "msg_def012"}