AI Control & Human Handoff
Control when the AI answers and when a human operator takes over the chat
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.
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
- Open any chat in Chats (dashboard or mobile app) and click the AI toggle — it switches immediately.
- Or via API:
PATCH /api/chats/{chatId}with{ "isUnderAiControl": false }(headerX-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
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 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
- 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 ifaiDisableReasonwasauto, 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
PATCH /api/chats/{chatId} with { "assistantId": "new-assistant-uuid" }. The new assistant continues from the latest message; all history is preserved.
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
{
"type": "CHAT_MODE_CHANGED",
"payload": {
"chatId": "uuid",
"isUnderAiControl": false,
"aiDisableReason": "auto"
}
}Connect to wss://api.happ.tools/ws for real-time updates.
Next
- Tools & webhook actions — how
turnOffAiModefits with other tools - Channels — where handoffs happen across messengers
- Assistants — configure who answers before handoff