# AI Control & Human Handoff

Control when the AI answers and when a human operator takes over the chat

> Full REST + WebSocket API reference: https://api.happ.tools/reference

Every chat has an **AI control** toggle so your client's team can let the assistant run on autopilot or step in manually whenever they need to.

<AiControlPageDemo />

The screenshot shows a conversation right after the AI invoked `turnOffAiMode`: the **AI** switch in the chat header is off, a banner reminds the operator they're replying manually, and the last message comes from a teammate.

## Toggle AI mode [#toggle-ai-mode]

1. Open any chat in **Chats** (dashboard or mobile app) and click the **AI toggle** — it switches immediately.
2. Or via API: `PATCH /api/chats/{chatId}` with `{ "isUnderAiControl": false }` (header `X-Access-Token: happ_your_token_here`).

| State                                  | Behavior                                                     |
| -------------------------------------- | ------------------------------------------------------------ |
| **AI On** (`isUnderAiControl: true`)   | Assistant automatically responds to new messages             |
| **AI Off** (`isUnderAiControl: false`) | Assistant pauses; messages wait in the dashboard for a human |

When AI is off, `aiDisableReason` records why:

| Reason   | Description                                   |
| -------- | --------------------------------------------- |
| `manual` | A human disabled AI from the dashboard or API |
| `auto`   | The AI triggered `turnOffAiMode` to hand off  |

## Human handoff [#human-handoff]

The built-in `turnOffAiMode` tool is always available. When the AI decides it can't help, it calls the tool, `isUnderAiControl` is set to `false`, the chat surfaces in the dashboard as needing attention, and a `CHAT_MODE_CHANGED` WebSocket event notifies all clients. Wire up [Telegram Push Notifications](/docs/integrations/notifications) so the team's group is pinged on every handoff.

Guide it from the assistant prompt:

```
If the customer asks to speak to a human, or if you cannot resolve
their issue after 2 attempts, use the turnOffAiMode tool to transfer
the conversation to a human operator. Tell the customer that a team
member will respond shortly.
```

## Auto-resume and context window [#auto-resume-and-context-window]

* After an **auto** handoff, AI re-enables after \~**10 minutes of inactivity**. A sweep finds messages older than 10 minutes, moves them to a context archive (assigns a `contextId`), and if `aiDisableReason` was `auto`, re-enables AI with a fresh context. **Manual** disables never auto-resume — a human must re-enable.
* The same 10-minute window bounds context: the AI only sees the last \~10 minutes of messages. Older ones stay in the DB but aren't sent to the model, so returning customers are treated as a fresh conversation.

## Transfer between assistants [#transfer-between-assistants]

`PATCH /api/chats/{chatId}` with `{ "assistantId": "new-assistant-uuid" }`. The new assistant continues from the latest message; all history is preserved.

## Message debouncing [#message-debouncing]

Rapid bursts are batched: **4-second debounce** after each message, **7-second maximum** before processing. Only the last message in a burst triggers a response, so the assistant sees the full thought.

## WebSocket events [#websocket-events]

```json
{
  "type": "CHAT_MODE_CHANGED",
  "payload": {
    "chatId": "uuid",
    "isUnderAiControl": false,
    "aiDisableReason": "auto"
  }
}
```

Connect to `wss://api.happ.tools/ws` for real-time updates.

**API:** [api.happ.tools/reference](https://api.happ.tools/reference)

## Next [#next]

* [Tools & webhook actions](/docs/tools) — how `turnOffAiMode` fits with other tools
* [Channels](/docs/channels) — where handoffs happen across messengers
* [Assistants](/docs/assistants) — configure who answers before handoff
