# Streaming an answer

`POST /api/v1/ask` streams. The response is a sequence of lines, each one a complete JSON object
carrying a `type`. Read it line by line and act on the types you care about.

```sh
curl -N -X POST https://api.bighugger.com/v1/ask \
  -H 'authorization: Bearer $BIGHUGGER_API_KEY' \
  -H 'content-type: application/json' \
  -d '{"question": "which small models run well on an iPhone?"}'
```

`-N` matters: without it curl buffers, and you get the whole answer at the end rather than as it
is written.

## Event types

| `type` | Fields | When |
| --- | --- | --- |
| `thread` | `id`, `title` | First. The thread this question opened. |
| `status` | `stage` | The run moved to a new stage: `planning`, `searching`, then reading. |
| `plan` | `queries` | The searches the question was turned into. |
| `sources` | `hits[]` — each with `id`, `title`, `url`, `kind` | What retrieval found, before the answer is written. |
| `tool` | `name`, `input` | The agent started a tool call. |
| `tool_done` | `error` | That call finished; `error` is a boolean. |
| `text` | `delta` | A fragment of the answer. Concatenate these in order. |
| `done` | `thread_id`, `workbook_url`, `sources` | Last on a successful run. |
| `error` | `error`, `message` | The run failed. See [Errors](/docs/errors). |

## Reading the stream

Two rules make a client that keeps working:

**Concatenate `text` deltas, in order.** They are fragments, not sentences. A `delta` may split a
word.

**Skip types you do not recognise.** New event types are added over time and are not a breaking
change. A client that throws on an unknown `type` will break on a release that adds one; a client
that ignores it will not.

A minimal reader:

```js
const res = await fetch("https://api.bighugger.com/v1/ask", {
  method: "POST",
  headers: {
    authorization: `Bearer ${process.env.BIGHUGGER_API_KEY}`,
    "content-type": "application/json",
  },
  body: JSON.stringify({ question: "which small models run well on an iPhone?" }),
});

let answer = "";
for await (const line of lines(res.body)) {
  const event = JSON.parse(line);
  switch (event.type) {
    case "text":    answer += event.delta; break;
    case "sources": console.log(`${event.hits.length} sources`); break;
    case "done":    console.log(answer); break;
    case "error":   throw new Error(`${event.error}: ${event.message}`);
    // Anything else is ignored on purpose.
  }
}
```

## Endings

A run ends with exactly one of `done` or `error`. A stream that stops without either was cut in
transit — treat it as a failure and retry, remembering that a retried question is billed again.

The whole run is billed once when it finishes, however many stages it passed through.
