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

# Chat

> Query your Unbound usage data with natural language from the terminal

`unbound chat` lets you ask questions about your AI usage data in natural language. It renders charts and tables directly in the terminal.

Requires **Admin or Manager** role. Run `unbound status` to check.

## Conversations are two-sided

The assistant can answer, and it can ask. A question back is a normal reply, not an error — the turn succeeds and exits `0`, and you answer it in the next turn.

Every turn belongs to a conversation, and each one builds on the ones before it, so you can refine an answer instead of restating it. There are two ways to hold one:

* **Interactive session** — context is kept for you.
* **One-shot commands** — pass the conversation id back with `-c`.

## Interactive session

Start a multi-turn REPL:

```bash theme={null}
unbound chat
```

Session commands:

| Command          | Action                                                                        |
| ---------------- | ----------------------------------------------------------------------------- |
| `/help`          | Show available commands                                                       |
| `/json`          | Print the last result as JSON                                                 |
| `/export <path>` | Write the last result to a file, as the JSON transcript. Refuses to overwrite |
| `/clear`         | Reset conversation history in memory                                          |
| `/exit`          | Quit (Ctrl+C also works)                                                      |

## One-shot mode

Ask a single question and exit:

```bash theme={null}
unbound chat -m "show cost by provider for the last 30 days"
unbound chat -m "which users spent the most this month"
unbound chat -m "MCP tool calls by server last week"
```

Each `-m` is its own process and sends no history of its own.

### Continuing a conversation

A reply that produced an answer ends with the command to continue it:

```
● Here's total cost by provider for the last 7 days...

  continue: unbound chat -m "..." -c <conversation_id>
```

Pass that id back with `-c` and the turns are read from the server, so the follow-up knows what came before:

```bash theme={null}
unbound chat -m "now just the top 2" -c <conversation_id>
```

This is also how you answer a question the assistant asked you.

With `--json` the id is in the payload as `conversation_id`. The same `continue:` line is printed to your terminal on stderr, so it stays readable without entering the stream — it is skipped when stderr is redirected, since a script reads the id off the payload:

```bash theme={null}
id=$(unbound chat -m "cost by provider last 7 days" --json | jq -r .conversation_id)
unbound chat -m "now just the top 2" -c "$id"
```

<Note>
  A conversation id that matches nothing is not an error — the question is still answered, just without the earlier context. The reply says so on stderr and sets `resumed` to `false`.
</Note>

<Tip>
  A single turn can answer several things at once, and will say plainly what it could not do. Splitting a broad request across turns with `-c` tends to give sharper answers though, and is easier to check. Messages are capped at 4,000 characters.
</Tip>

## Output flags

| Flag                  | Behavior                                                                  |
| --------------------- | ------------------------------------------------------------------------- |
| `--json`              | Write the JSON transcript to stdout                                       |
| `-o <path>`           | Write the JSON transcript to a file (refuses to overwrite existing files) |
| `-c, --continue <id>` | Continue the conversation with this id                                    |

```bash theme={null}
# Pipe to jq:
unbound chat -m "cost by provider last 30 days" --json \
  | jq -r '.blocks[] | select(.type=="text") | .text'
unbound chat -m "cost by provider last 30 days" --json \
  | jq '.blocks[] | select(.type=="chart") | .rows'

# Save to file:
unbound chat -m "cost by provider last 30 days" -o report.json

# Pipe and archive simultaneously:
unbound chat -m "cost by provider last 30 days" --json -o report.json | jq '.blocks | length'
```

<Note>
  With `--json`, stdout carries the transcript and nothing else — confirmations and hints go to stderr so the stream stays parseable. Requires CLI **1.13.0** or later.
</Note>

### What the CLI produces

JSON, and charts rendered in the terminal. `--json`, `-o` and `/export` all write the same transcript, with chart data as `columns` and `rows` — ready to turn into a CSV yourself.

The CLI does not produce CSV or PNG files; those are download buttons in the web UI. PowerPoint, Slides and PDF are not available anywhere. If the assistant mentions CSV or PNG in its reply, it is describing the web UI, not this command.

## JSON response shape

```json theme={null}
{
  "success": true,
  "error": null,
  "conversation_id": "<uuid>",
  "continue_command": "unbound chat -m \"...\" -c <uuid>",
  "log_id": "<uuid>",
  "truncated": false,
  "resumed": false,
  "blocks": []
}
```

| Field              | Meaning                                                                                                                                              |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `success`          | `false` when the turn failed                                                                                                                         |
| `error`            | The reason when `success` is `false`, otherwise `null`                                                                                               |
| `conversation_id`  | The conversation this turn belongs to. Pass it to `-c` to continue                                                                                   |
| `continue_command` | The next command spelled out, for a reader that should act on it rather than build one from `conversation_id`. `null` when the turn produced nothing |
| `log_id`           | Identifies the stored turn, for feedback and for support to trace                                                                                    |
| `truncated`        | The agent ran out of budget partway. The blocks present are real — ask a follow-up for the rest                                                      |
| `resumed`          | Whether `-c` found the conversation. `false` means the turn ran without the earlier context                                                          |
| `blocks`           | The answer, in the order it was produced                                                                                                             |

A turn is an ordered transcript, so one question can return several charts with prose between them. Each block is either text or a chart:

```json theme={null}
{ "type": "text", "text": "Here's total cost broken down by provider..." }
```

```json theme={null}
{
  "type": "chart",
  "title": "Cost by Provider (Last 30 Days)",
  "chart_type": "bar",
  "row_count": 7,
  "columns": ["dimension", "metric_value"],
  "rows": [{ "dimension": "Anthropic", "metric_value": 128.4 }],
  "view_spec": {}
}
```

`rows` is abbreviated above; a real chart carries every row it found.

Charts arrive decoded — `columns` and `rows`, not a chart-library option — and `view_spec` is the query that produced them, for reuse or refinement.

`chart_type` is one of `bar`, `line`, `area`, `stacked_bar`, `pie`, `donut`, `scatter`, `heatmap`, `kpi` or `table`.

A chart block carries `decode_failed: true` only when its data could not be read, so empty `rows` from a broken payload are distinguishable from a genuinely empty answer.

## Exit codes

| Code | Meaning                                                                                                          |
| ---- | ---------------------------------------------------------------------------------------------------------------- |
| `0`  | Success, including a turn that ends by asking you a question                                                     |
| `1`  | No blocks returned, auth failure, role error, network failure, a message over 4,000 characters, or a write error |
