Appearance
Chats, AI mode, and channel health from Claude or Codex
From Claude Code, Codex, or any MCP client connected to the MyChatBot plugin or to the Sales-Management MCP you can:
- decide who answers a chat: the AI, or a person (
chat_set_mode,sales_conversations); - give back to the AI the chats that have sat with a manager for too long, many at once, after a preview (
chat_bulk_return_to_ai,sales_conversations); - see how each channel is doing: on or off and why, when customers last wrote, open disconnect alerts (
channel_get_health,sales_channels).
Every parameter and field is in the Sales tools reference. All three are yours: your own connection reaches them, and the session MyChatBot support gets when you ask it for a change does not (see Who can use which tool). A refused call's code is explained in Errors and refusals.
Who answers a chat
Every chat is in one of two modes, the same ones as the AI switch at the top of a chat in Active chats:
| Mode | What happens |
|---|---|
ai | The assistant answers the customer. |
manager | The assistant stays silent; a person answers from the dashboard or the channel's own app. The chat shows on the Operator tab. |
chat_set_mode switches one chat. Find its chat_id with list_chats (filter by needs_operator: "yes" to see the chats with a person), then preview:
json
{ "chat_id": "568507", "mode": "ai", "dry_run": true }json
{
"chat_id": "568507",
"assistant_id": "909",
"communication_channel": "Instagram",
"mode_before": "manager",
"mode": "ai",
"changed": false,
"dry_run": true,
"message": "dry run: nothing was changed. Without dry_run the chat goes to ai mode and leaves the Needs-attention queue."
}Without dry_run the switch is made exactly as the dashboard's AI switch makes it:
- the chat changes mode and leaves the Attention queue (
needs_attention_cleared: true); - its timeline shows "Chat switched to AI mode" (or "to operator mode"), with the reason Given back to the AI over the Sales MCP (or Handed to a manager over the Sales MCP);
- handing a chat to a manager counts as a switch made by hand: the assistant's auto-return brings it back after the interval unless Keep chats switched off by hand in operator mode is checked, a Telegram funnel the customer is in stops, and if the assistant assigns leads to managers automatically, the lead is assigned by those rules, as when the assistant hands a chat over itself.
Pass assistant_id too when you want the call refused unless the chat is that assistant's. A chat that already is in the mode you ask for is left as it is (changed: false).
The AI answers the next message, not the past ones
Giving a chat back to the AI does not answer what the customer wrote while it was with a person. The assistant replies to their next message. To answer now, reply yourself, or use Run assistant once in the chat.
Give stuck chats back to the AI
A chat handed to a person stays with them until someone gives it back. When nobody answers, the customer waits. chat_bulk_return_to_ai finds those chats and gives them back to the AI, each one as the dashboard's AI switch would, in two calls.
1. Preview (the default)
Call it with the filters you want. Without dry_run: false it never changes anything:
json
{ "assistant_id": "909", "communication_channel": "Instagram", "quiet_hours": 48 }| Filter | Meaning |
|---|---|
assistant_id | Only this assistant's chats |
communication_channel | Only chats on this channel type, as list_channels names it (any letter case) |
page_id | Only chats on one channel |
quiet_hours | Only chats where nobody on your team has written for at least this many hours (24 by default; 0 takes every chat with a person) |
include_escalations | Also take the chats the assistant handed to a person itself and nobody has answered yet (off by default) |
limit | The most chats one call gives back: 50 by default, at most 100 |
Quiet hours count from the later of two moments: when the chat was handed to a person, and the last message your side wrote in it (a manager's reply, from the dashboard or from the channel's own app, or the assistant's last reply before the hand-over). A chat with neither in the last 90 days counts as quiet for at least 90 days. A chat whose messages cannot be read (it has no channel or contact id) is never taken by a quiet-hours filter: a person may be writing in it right now. Only quiet_hours: 0 takes it.
The answer lists the chats, the quietest first:
json
{
"dry_run": true,
"matched": 37,
"selected": 37,
"limit": 50,
"quiet_hours": 48,
"more_matching": false,
"chat_ids": ["551203", "551877", "560114", "..."],
"customers_waiting": 12,
"customers_waiting_chat_ids": ["551203", "..."],
"sample": [
{
"chat_id": "551203",
"client_name": "Olena",
"communication_channel": "Instagram",
"handed_over_at": "2026-10-20T09:14:02Z",
"team_last_wrote_at": "2026-10-20T09:20:45Z",
"customer_last_wrote_at": "2026-10-21T17:02:11Z",
"customer_waiting": true,
"quiet_hours": 128,
"escalated_by_assistant": false
}
],
"left_with_manager": { "assistant_escalations": 4, "spam": 1, "quiet_unknown": 0 },
"scanned": 212,
"scan_truncated": false,
"preview_token": "1761401234.Xx…",
"next": "To give these 37 chats back to the AI, call chat_bulk_return_to_ai again with the same filters, dry_run=false, chat_ids exactly as listed and this preview_token (valid 30 minutes)."
}matchedis how many chats qualify;selectedhow many this call would give back (at mostlimit);more_matching: truemeans another round is needed after this one.customers_waitingcounts the listed chats whose customer wrote after your side last did, andcustomers_waiting_chat_idsnames them (customer_waiting: truein the sample). Given back, they also leave the Attention queue, and the AI answers only the customer's next message: answer those first, or leave them out with narrower filters.left_with_managercounts the chats that matched but stay with a person:assistant_escalations(the assistant asked for a person and nobody has answered; those customers were told someone is coming),spam(never given back in bulk), andquiet_unknown(their messages cannot be read, see above).- The sample shows up to 20 chats. It carries customer names: share it only with people who may see the chats.
2. Give them back
Check the list, then call again with the same filters, dry_run: false, the chat_idsexactly as listed, and the preview_token:
json
{
"assistant_id": "909",
"communication_channel": "Instagram",
"quiet_hours": 48,
"dry_run": false,
"chat_ids": ["551203", "551877", "560114", "..."],
"preview_token": "1761401234.Xx…"
}json
{
"dry_run": false,
"requested": 37,
"returned_to_ai": 35,
"returned_chat_ids": ["551203", "..."],
"skipped": { "team_wrote_since": ["560114"], "already_with_ai": ["561002"] },
"message": "35 chats are with the AI again; the assistant answers each customer's next message. 2 were skipped because they changed since the dry run (see skipped)."
}Only the listed chats can change. Right before each one is changed, the chat and its messages are read again. A chat in which someone on your team wrote, or that was handed over again, after the preview is skipped, whatever quiet_hours is (with quiet_hours: 0 too). So is a chat that no longer qualifies. Each skipped chat is named under skipped:
| Reason | Meaning |
|---|---|
already_with_ai | Someone gave it back already |
team_wrote_since | Someone on your team wrote in it, or it was handed over again, after the preview (or it is no longer quiet long enough) |
filters_no_longer_match | It no longer matches the filters you gave, for example it moved to another assistant |
assistant_escalation / spam | It was escalated by the assistant or marked as spam since |
quiet_unknown | Its messages can no longer be read |
not_found | It is not one of this account's chats |
A chat listed under failed_chat_ids could not be saved; run the preview again to retry it (a chat that did switch meanwhile is no longer listed).
The token is good for 30 minutes and only for this account, these filters, and this list of chats. A different list, other filters, or an old token is refused with preview_required: run the preview again. Running the same call twice gives nothing back the second time.
To give back more than 100 chats, preview and give back again: the chats already with the AI no longer match.
Channel health
channel_get_health shows how each channel is doing, from what MyChatBot has stored. It makes no live call to Meta, WhatsApp, or any other platform, so a change made there shows here only once the platform has told MyChatBot about it. Filter by assistant_id, communication_channel, or page_id; one call covers up to 50 channels. Credentials and tokens are never part of the answer.
json
{
"channels": [
{
"communication_channel": "PersonalWhatsApp",
"page_id": "380501112233@c.us",
"assistant_id": "909",
"is_on": false,
"status": "paused",
"status_reason": "paused by MyChatBot: WhatsApp logged this number out; scan the QR code again in the dashboard",
"paused_at": "2026-10-23T08:00:00Z",
"assistant": { "name": "Sofiia", "is_on": true },
"last_activity_at": "2026-10-23T07:42:10Z",
"last_customer_message_at": "2026-10-23T07:41:58Z",
"last_reply_at": "2026-10-23T07:42:10Z",
"open_alerts": [
{ "kind": "logged_out_rescan_needed", "message": "…", "raised_at": "2026-10-23T08:10:00Z", "seen": false }
],
"personal_whatsapp": {
"session": "logged_out",
"authorized_at": "2026-09-01T10:00:00Z",
"paused_reason": "disconnected",
"paused_at": "2026-10-23T08:00:00Z",
"removal_due_at": "2026-10-30T08:00:00Z"
}
}
],
"count": 1,
"activity_window": "last_customer_message_at and last_reply_at come from each channel's 20 most recently active chats over the last 90 days; null means none there",
"checked_at": "2026-10-25T12:00:00Z"
}What each field means
| Field | Meaning |
|---|---|
status | working (on, with its assistant on); needs_attention (working, with an open disconnect alert); assistant_off (the channel is on but the assistant answering on it is off, deleted, or missing); paused (MyChatBot switched it off: Personal WhatsApp only); off (switched off); being_removed (a Personal WhatsApp number in the middle of being removed); unknown (on, but its assistant could not be read right now) |
status_reason | The same in words, with what brings it back where that is known |
is_on | The channel's own switch, as in the dashboard |
assistant | The assistant answering on the channel: is_on, and when off, off_reason as stored (insufficient_tokens, subscription_expired, outside_working_hours, moderation_flagged, plan_agent_limit, or null when switched off by hand) and reason in words |
last_activity_at | The latest activity in the channel's chats, in either direction |
last_customer_message_at | When a customer last wrote, among the channel's 20 most recently active chats in the last 90 days; null when none did there. Instagram and Messenger can share a page: each channel then counts only its own chats |
last_reply_at | When your side last answered there: the assistant, or a person from the dashboard or the channel's own app |
open_alerts | The channel's disconnect alerts that are not resolved yet (the ones in the dashboard's notifications): kind, the message you were sent, raised_at, and seen. The newest 2,000 alerts of the account are read; past that, alerts_note says so |
paused_at | When MyChatBot paused a Personal WhatsApp number |
personal_whatsapp.session | signed_in (as last recorded), logged_out (WhatsApp ended the session: scan the QR code again before removal_due_at, when the number is removed), never_scanned (the QR code was never scanned; such a number is removed after about an hour), being_removed |
Alert kinds: paused_for_balance, logged_out_rescan_needed, disconnected, instagram_account_changed, and instagram_not_linked.
What it cannot tell you
- Why another channel is off. Only Personal WhatsApp records why MyChatBot switched it off (the balance ran out, or WhatsApp logged the number out). Any other channel that is off was switched off in the dashboard or over MCP; a Calls channel follows its assistant's Work Hours and plan, and says so.
- Whether a token still works right now. Nothing is checked live. A channel whose platform stopped accepting it shows up through its open alert, once MyChatBot has noticed.
- Knowledge and integration alerts. Alerts about a knowledge base, product feed, or integration are left out: they are not about a channel, and many of them clear on the next successful refresh. Use
list_knowledge_sourcesandintegration_verifyfor those. - Which channel an alert about a switched Instagram account belongs to. When one of your pages switched to another Instagram account, the alert names the new account, which no channel stores. The account-wide answer (no filters) lists such alerts, and any left behind by a channel removed since, under
unmatched_open_alerts, so they are never dropped.
Typical requests
- "Which chats have been waiting for a manager since Friday? Give them back to the AI." —
chat_bulk_return_to_aiwithquiet_hours, show the preview, then give them back. - "Hand the chat with Olena to me." —
list_chatsto find it, thenchat_set_modewithmode: "manager". - "Why didn't my WhatsApp answer yesterday?" —
channel_get_healthwithcommunication_channel: "PersonalWhatsApp", thenlist_chatsandget_chat_messagesfor a chat in question.
See also
- Active chats — the AI switch, the Attention queue, and response timers
- Assistant settings, hours, and Flight Control from Claude or Codex — auto-return and hand-off settings
- Channels — connecting and switching channels
- Sales tools reference — every parameter and field
