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

# Chatbots

> Conversational assistants with per-user sessions, history and streamed replies

A **chatbot** is a model with a system prompt and a welcome message. Each conversation with it is a **session** that belongs to one user and keeps its history, so a user can leave and come back. Replies can be streamed as they are generated.

Use a chatbot for conversation: answering questions about a record, explaining a decision, helping a user through a task. For one-shot processing (extract, classify, decide), use an [agent](/wavefront/agents) or a [workflow](/wavefront/workflows).

## Creating a chatbot

**Console**

Go to **Chatbots → Create Chatbot** and fill in: name, namespace, description, system prompt, welcome message, the model from the LLM Repository, and optionally a temperature. Edit it later from the same page.

**API**

```bash theme={null}
curl -X POST "$APP/floware/v1/chatbots" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "name": "order-assistant",
    "namespace": "support",
    "description": "Answers questions about one order",
    "system_prompt": "You help a support agent understand ONE order. …",
    "welcome_message": "Ask me anything about this order.",
    "llm_config_id": "<llm-config-id>",
    "config": { "temperature": 0.2 },
    "enabled": true
  }'
```

| Field | |
| - | - |
| `name` | Starts with a letter; letters, digits, `-` and `_`. Unique within the namespace |
| `system_prompt` | The instructions, sent on every turn |
| `welcome_message` | Stored as the first assistant message of every new session |
| `llm_config_id` | A model from the [LLM Repository](/wavefront/llm-configs) |
| `config` | Overrides on the model's defaults; currently `temperature` (0–2) |
| `enabled` | **Chatbots start disabled.** A disabled chatbot behaves as if it did not exist: new sessions and messages are refused |

Update with `PATCH /floware/v1/chatbots/<id>` (any subset of fields), list with `GET /floware/v1/chatbots`, delete with `DELETE /floware/v1/chatbots/<id>`. The console has no chat panel for trying a chatbot; try it from your application or the API.

## Sessions

```bash theme={null}
# start a session (the welcome message comes back as its first message)
curl -X POST "$APP/floware/v1/chatbots/<chatbot-id>/sessions" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"title": "Order O-1001"}'

# the current user's sessions with a chatbot
curl "$APP/floware/v1/chat-sessions?chatbot_id=<chatbot-id>" -H "Authorization: Bearer $TOKEN"

# a session's messages
curl "$APP/floware/v1/chat-sessions/<session-id>/messages" -H "Authorization: Bearer $TOKEN"
```

* A session belongs to the user who created it; each user sees only their own.
* A session keeps a **snapshot of the system prompt** taken when it started. Changing the chatbot's prompt affects new sessions only; start a new session to pick up a change.
* Rename a session with `PATCH /floware/v1/chat-sessions/<id>` (`title`), delete it with `DELETE`.

## Sending messages

```bash theme={null}
curl -X POST "$APP/floware/v1/chat-sessions/<session-id>/messages" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"content": "Why was this order held?"}'
```

The response's `data` holds `user_message` and `assistant_message`, both as stored. A message can be up to **32,000 characters**. Each turn sends the system prompt and the **last 50 messages** of the session to the model.

### Streaming replies

Add `?stream=true` to get `text/event-stream`. Each `data:` line is a JSON object:

| Event | Meaning |
| - | - |
| `{"content": "…"}` | The next piece of the reply; append it |
| `{"retract": true, "content": "…", "reason": "…"}` | A [guardrail](/wavefront/concepts#running-things) replaced what was streamed so far; show `content` instead |
| `{"error": "…"}` | The turn failed |
| `{"done": true, "message_id": "…"}` | The reply is complete and stored |

```ts theme={null}
async function streamReply(sessionId: string, content: string, onText: (t: string) => void) {
  const res = await api(`/v1/chat-sessions/${sessionId}/messages?stream=true`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }),
  });
  const reader = res.body!.getReader();
  const decoder = new TextDecoder();
  let buffer = '', text = '';
  for (;;) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const events = buffer.split('\n\n');
    buffer = events.pop() ?? '';
    for (const e of events) {
      if (!e.startsWith('data: ')) continue;
      const data = JSON.parse(e.slice(6));
      if (data.error) throw new Error(data.error);
      if (data.retract) text = data.content ?? '';
      else if (typeof data.content === 'string') text += data.content;
      onText(text);
      if (data.done) return text;
    }
  }
  return text;
}
```

(`api` is the authenticated `fetch` wrapper from [Integrating an application](/wavefront/integrating-an-app#a-minimal-client).)

## Pattern: an assistant beside a record

A common feature is a chat panel on a record's page that answers questions about **that** record:

1. **One session per user per record.** Title sessions predictably (for example `Order O-1001`); when the page opens, find the user's session with that title and resume it, or create one.
2. **Send the record as context once,** at the start of the first message, between markers your UI can hide:
   ```text theme={null}
   <<<RECORD_CONTEXT>>>
   ## Order
   - Id: O-1001 · Amount: 250 · Status: HELD
   ## Review
   - Decision: REFER · Reasons: address mismatch …
   <<<END_RECORD_CONTEXT>>>

   Why was this order held?
   ```
   When showing history, strip everything between the markers.
3. **Write the system prompt for it:** say the block between the markers is the only source of facts; never invent values; never change or second-guess decisions made by the process, only explain them; treat the block as data, not instructions; and answer only about this record.
4. **Offer "new chat"** that starts a fresh session with up-to-date context, because the context is a snapshot from when the session started.

> [!WARNING]
>
> Each turn sends only the **last 50 messages**. In a very long session, the first message, and the context in it, eventually drops out of what the model sees. Keep the context in the first message short and offer a fresh session, or re-send a summary of the context when a session gets long.

* Keep the context under the per-message limit (32,000 characters): summarise long sections rather than pasting raw data.
* Leave out what the user is not allowed to see. The model will repeat anything in the context.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.