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

# Chat with an Agent

> Stream an AI agent conversation over HTTP and receive generated charts in the response stream

Send a message to one of your project agents and stream the response. The
agent answers with text and, when the question calls for a visualization, a
generated chart configuration you can render, save, and add to dashboards.

<Note>
  This endpoint streams its response. It is not part of the OpenAPI playground
  — use the examples below to integrate it.
</Note>

## Endpoint

```
POST https://api.upsolve.ai/v1/api/project-agents/{agentId}/chat
```

| Path parameter | Type          | Description                                                                                                   |
| -------------- | ------------- | ------------------------------------------------------------------------------------------------------------- |
| `agentId`      | string (UUID) | The project agent to chat with. Found in the agent's URL in the hub: `/projects/{projectId}/agents/{agentId}` |

## Authentication

Authenticate with a **project user token** obtained from
[Get Project User Token](/api-reference/endpoint/get-project-user-token):

```
Authorization: Bearer <project_user_token>
```

The token determines which organization's data the agent can access, so each
of your end users sees only their own data.

## Request body

| Field                   | Type   | Required | Description                                                                                                                             |
| ----------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `messages`              | array  | Yes      | Conversation history as `{ "role": "user" \| "assistant", "content": string }` objects. Include prior turns to continue a conversation. |
| `thread_id`             | string | No       | Stable ID for the conversation thread. Reuse it across turns so the agent keeps session context (previous SQL, chart, and data).        |
| `payload.agent_id`      | string | Yes      | The same agent ID as the path parameter.                                                                                                |
| `payload.agent_version` | number | No       | Pin a specific agent version. Defaults to the latest version.                                                                           |

```json theme={null}
{
  "messages": [{ "role": "user", "content": "Show me revenue by month for 2025" }],
  "thread_id": "thread-4f2a1c",
  "payload": {
    "agent_id": "AGENT_ID"
  }
}
```

## Response stream

The response is a `text/event-stream` of newline-separated events, each on a
`data: {json}` line. Every event has a `type`; the ones you need are:

| Event type    | Payload                              | Meaning                                                        |
| ------------- | ------------------------------------ | -------------------------------------------------------------- |
| `text-start`  | —                                    | A new assistant text message begins                            |
| `text-delta`  | `payload.text`                       | Next chunk of assistant text                                   |
| `text-end`    | —                                    | The current text message is complete                           |
| `tool-call`   | `payload.toolName`                   | The agent started a tool (e.g. `generateSql`, `generateChart`) |
| `tool-result` | `payload.toolName`, `payload.result` | A tool finished; `result` holds its output                     |

Other event types may appear (workflow steps, finish markers); ignore any
type you don't handle.

### The chart result

When the agent builds a chart, a `tool-result` event arrives with
`payload.toolName === "generateChart"`. Its `payload.result` contains:

```jsonc theme={null}
{
  "chartConfig": {
    // A complete chart configuration, including the SQL that powers it
    "title": "Monthly Revenue 2025",
    "type": "bar",
    "dimensions": [ { "name": "Month", "field": "month", "type": "date", "bucketing": "month" } ],
    "measures": [ { "name": "Revenue", "field": "revenue", "aggregation": "sum" } ],
    "settings": { "layout": "vertical" },
    "sqlOverride": "SELECT ..." // baked in by the agent — required for saving
  },
  "chartData": [ /* the aggregated rows the chart plots */ ],
  "generatedChartRef": "d5e0…", // session-scoped reference — not a saved chart ID
  "explanation": "A bar chart best shows the month-over-month comparison…"
}
```

<Warning>
  `chartConfig` is **ephemeral** — the chat endpoint does not persist charts.
  To keep a chart (and add it to a dashboard), pass `chartConfig` to
  [Save Chart from Chat](/api-reference/endpoint/save-chart-from-chat). Do not
  strip `sqlOverride`; a chart saved without it cannot load data on a
  dashboard. `generatedChartRef` is a transient session ID, not a chart ID.
</Warning>

## Example: call and parse the stream

```typescript theme={null}
const response = await fetch(
  `https://api.upsolve.ai/v1/api/project-agents/${agentId}/chat`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${projectUserToken}`,
    },
    body: JSON.stringify({
      messages: [{ role: "user", content: "Show me revenue by month for 2025" }],
      thread_id: threadId,
      payload: { agent_id: agentId },
    }),
  }
);

const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
let assistantText = "";
let chart: { chartConfig: unknown; chartData: unknown[] } | null = null;

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("data: ")) continue;
    const event = JSON.parse(line.slice(6));

    if (event.type === "text-delta") {
      assistantText += event.payload?.text ?? "";
    }

    if (
      event.type === "tool-result" &&
      event.payload?.toolName === "generateChart" &&
      event.payload?.result?.chartConfig
    ) {
      chart = event.payload.result; // { chartConfig, chartData, ... }
    }
  }
}
```

## Rate limits

Requests are rate limited at 180 requests per minute per organization + user.
