# Chat

```
POST /api/v1/{organisation}/widgets/{widget}/chat
```

**Auth:** widget domain, widget secret, or organisation API key with `chat`  
**Widget type:** chatbot  
**Billing:** uses one answer from your plan. When you have no answers left, the API returns **402** with free search hits in `data` (same shape as the search endpoint). No chat model is called. The official embed renders those results in the bubble.

## Body

| Field | Type | Required | Description |
|---|---|---|---|
| `query` | string | yes | Visitor message, max 2048 characters |
| `conversation_id` | string | no | Continue an existing conversation |
| `session_id` | string | no | Analytics session |
| `browser_language` | string | no | Language hint from the visitor's browser |
| `context` | object | no | Up to 10 string keys with simple values (for example page URL or title) |
| `user` | object | no | `{ "id": "…", "hash": "…" }` signed visitor identity |
| `stream` | bool | no | Set `true` for a streamed response instead of one JSON payload |

## JSON response (`stream` false or omitted)

```json
{
  "data": {
    "text": "…answer in markdown…",
    "conversation_id": "…",
    "answer_id": "…",
    "sources": [
      {
        "id": "0194b…",
        "external_id": "wp-42",
        "name": "Hello world",
        "url": "https://example.com/hello-world",
        "snippet": "…",
        "score": 0.91,
        "meta": null
      }
    ]
  },
  "session_id": "…"
}
```

## Streaming (`"stream": true`)

The response is a stream of server-sent events (`text/event-stream`):

| Event | Data | When |
|---|---|---|
| `status` | `{ "step": "searching", "query": "…" }` | Looking up sources |
| `token` | `{ "text": "…" }` | Next piece of the answer |
| `done` | `{ "conversation_id", "session_id", "answer_id", "sources": […] }` | Finished |
| `error` | `{ "message": "…" }` | Something failed mid-stream |

## Identity and conversation ownership

- With a valid `user.id` and `user.hash`, the conversation belongs to that verified visitor.
- Continuing a verified conversation without the same proof returns **403**.
- A bad hash returns **422**.

See [Conversations](/docs/conversations) for listing history, reading transcripts, keeping conversations, and deletion.
