> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trellis.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# 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).

<Note>
  For a complete guide on handling responses, see the [Chat Overview](/api-reference/chats/overview).

  Prefer real-time bidirectional communication? Use the [WebSocket endpoint](/api-reference/chats/websocket) instead.
</Note>

<Warning>
  `chat_id` is now required. Call [Create Chat](/api-reference/chats/create-chat) first to obtain one — sending without `chat_id` returns `400 MISSING_CHAT_ID`, and an unknown ID returns `404 CHAT_NOT_FOUND`.
</Warning>

<ParamField header="Authorization" type="string" required>
  Bearer token obtained from the authenticate endpoint.
</ParamField>

<ParamField header="Accept" type="string">
  Set to `text/event-stream` to receive SSE responses.
</ParamField>

<ParamField body="message" type="string" required>
  The natural language message to send.
</ParamField>

<ParamField body="chat_id" type="string" required>
  ID of an existing chat. Obtain one from [Create Chat](/api-reference/chats/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`.
</ParamField>

<ParamField body="integration_id" type="string">
  ID of the database integration to query. Required for database queries.
</ParamField>

<ParamField body="title" type="string">
  Custom title for new chats. If omitted, a title is generated automatically.
</ParamField>

<ParamField body="model" type="string">
  AI model to use for this message. If omitted, the default model is used. See [List Models](/api-reference/models) for available values.
</ParamField>

<ParamField body="upload_ids" type="string[]">
  List of upload IDs to attach to this message. Upload files first via [Upload Files](/api-reference/chats/upload-files), then reference their IDs here. Requires `chat_id` to be set.
</ParamField>

<ParamField body="saved_query_id" type="string">
  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](/api-reference/queries/list-queries) to retrieve query IDs.
</ParamField>

<ParamField body="parameters" type="object">
  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.

  ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "year": "2025", "limit": "10" }
  ```
</ParamField>

<Note>
  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.
</Note>

## Response

The response is a stream of Server-Sent Events. See [Chat Overview](/api-reference/chats/overview) for details on handling the stream.

### Event types

| Event           | Data                                                                                            | Description                                                                                                                                                                         |
| --------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chat_metadata` | `{"id": "chat_...", "user_message_id": "msg_..."}`                                              | Chat ID and persisted user message ID. Emitted at the start of every response.                                                                                                      |
| `processing`    | `{"status": "starting" \| "thinking" \| "analyzing" \| "processing"}`                           | AI processing status. `starting` is emitted within \~100ms of request receipt.                                                                                                      |
| `visualization` | Full chart or table payload                                                                     | A chart or table was generated. See [Visualizations](/api-reference/chats/visualizations).                                                                                          |
| `text_delta`    | `{"content": "..."}`                                                                            | 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.            |
| `error`         | `{"error": "...", "code": "...", "retryable": boolean, "debug_id": "...", "error_type": "..."}` | An error occurred. See [WebSocket → Error codes](/api-reference/chats/websocket#error-codes) for the stable `code` enum and retry semantics.                                        |

<Note>
  `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](/api-reference/chats/overview#streaming-text-deltas) for the buffer-and-reconcile pattern.
</Note>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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"
    }'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import requests
  import json

  response = 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 = None
  for 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
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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 = "";
      }
    }
  }
  ```
</RequestExample>

<ResponseExample>
  ```text Text-only response theme={"theme":{"light":"github-light","dark":"github-dark"}}
  event: chat_metadata
  data: {"id": "chat_abc123", "user_message_id": "msg_001"}

  event: processing
  data: {"status": "thinking"}

  event: processing
  data: {"status": "analyzing"}

  event: text_delta
  data: {"content": "There were 1,523 orders "}

  event: text_delta
  data: {"content": "placed last month (December 2024)."}

  event: message
  data: {"content": "There were 1,523 orders placed last month (December 2024).", "id": "msg_xyz789"}
  ```

  ```text Response with bar chart theme={"theme":{"light":"github-light","dark":"github-dark"}}
  event: chat_metadata
  data: {"id": "chat_def456", "user_message_id": "msg_001"}

  event: processing
  data: {"status": "thinking"}

  event: processing
  data: {"status": "analyzing"}

  event: visualization
  data: {"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: message
  data: {"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"}
  ```

  ```text Response with table theme={"theme":{"light":"github-light","dark":"github-dark"}}
  event: chat_metadata
  data: {"id": "chat_ghi789", "user_message_id": "msg_001"}

  event: processing
  data: {"status": "analyzing"}

  event: visualization
  data: {"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: message
  data: {"content": "Here are the active projects with their current status and budgets.", "id": "msg_def012"}
  ```

  ```text Error theme={"theme":{"light":"github-light","dark":"github-dark"}}
  event: error
  data: {"error": "Datasource not found", "code": "INTEGRATION_NOT_FOUND", "retryable": false, "debug_id": "4a8c0ae6", "error_type": "ValueError"}
  ```

  ```json 400 Missing chat_id theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": {
      "code": "MISSING_CHAT_ID",
      "message": "Provide chat_id; create one via POST /v1/chats/create"
    }
  }
  ```

  ```json 404 Chat not found theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "detail": {
      "code": "CHAT_NOT_FOUND",
      "message": "chat_id does not exist"
    }
  }
  ```
</ResponseExample>
