Chat Handoff to Human Agents
Overview
When the bot cannot answer, or the user types something like "talk to an agent", the system has to switch from bot-driven replies to human-driven ones. The conversation is forwarded to Mbox — a Chatwoot-style live chat platform — and auto-response must be prevented from interrupting while an agent is engaged.
The "currently talking to an agent" state lives in Redis under
agent_mode:<lineOaId>:<userId>, paired with the agent_sessions sorted set that acts as the
expiry clock for every open session.
Business Flow
Handoff mode — entering agent mode
- Receive an
MboxHandoffPayloadcarrying anmboxConfigblock: base URL, API token, account ID, inbox ID, LINE webhook URL, timeout and warning durations, the greeting / warning / end / timeout message templates, and the list of exit keywords. - Deduplicate — if
agent_mode:<lineOaId>:<userId>already exists, the user is already in agent mode and the job is skipped. - Forward the webhook into Mbox so a conversation is created on that side.
- The stored webhook at
mbox_pending_webhook:<lineOaId>:<userId>is preferred, because a postback event cannot create a conversation in Mbox while the original message that triggeredtalk_to_agentcan. - If nothing is stored, the current webhook is used instead. A failed forward is not fatal — it is logged and processing continues. The HTTP timeout is 5 seconds.
- The stored webhook at
- Write the Redis
agent_modehash with the full config,warningSentset to 0, and the start timestamp. - Add the member to the
agent_sessionssorted set with the score set to the epoch time at which the warning should be sent. - Push the greeting message to the user through the LINE API.
Department picker mode
The service sends a flex message listing the available departments and stores the current
webhook under mbox_pending_webhook, so it can be replayed during the real handoff once the
user picks a department.
Exit mode
When the user types one of the configured exit keywords, the service resolves the conversation
in Mbox, deletes the agent_mode key, removes the member from agent_sessions, and sends the
closing message.
Timeout cron (profile cron-scheduler, every minute)
MboxTimeoutService.Run pulls every member of agent_sessions whose score has come due and
handles it in two stages.
- Stage 1 (
warningSentis 0) — send the "still waiting for an agent" warning, setwarningSentto 1, and push the score forward by(timeoutMinutes - warningMinutes) * 60seconds. - Stage 2 (
warningSentis 1) — the session has genuinely timed out: resolve the Mbox conversation, delete theagent_modekey, remove the member from the sorted set, and send the timeout message. - Inconsistent state or a missing hash simply gets swept out of the sorted set.
Key Files & Functions
| File | Responsibility |
|---|---|
internal/mbox/service_handoff.go | MboxHandoffService.HandleHandoff, HandleDepartmentPicker, HandleExit, ResolveMboxConversation, forwardWebhook, sendFlexMessage, sendLineMessage, channelAccessToken, plus the MboxConfig and MboxHandoffPayload types and the 5-second forwardTimeout constant |
internal/mbox/consumer.go | Consumer.HandleMboxHandoff — dispatches on the payload type between exit, department_picker and the default handoff mode |
internal/cronscheduler/mbox_timeout.go | MboxTimeoutService.Run, processExpiredMember, sendLineMessage |
internal/mboxx/mboxx.go | Mbox REST client, authenticating with the api_access_token header |
cmd/worker/main.go | runCronScheduler — wires the consumer and the cron jobs together |
Queue: mbox_handoff on the cron-scheduler profile.
Connections to Other Services
- line-management-webhook-go — matches keywords against the cached
webhook_configand publishes the job. - Action Execution — the
talk_to_agentaction is the other producer for this queue. - Redis (central to the design) —
agent_mode:<lineOaId>:<userId>holds the session hash,agent_sessionsis the expiry sorted set, plusmbox_pending_webhook:<lineOaId>:<userId>,mbox_inbox:<inboxId>andwebhook_config:<webhookId>. - Table
line_oa— supplieschannel_access_tokenandmessage_handling_config.mbox. - Mbox REST API — creates and resolves conversations and assigns teams, with a 5-second timeout.
- LINE API —
POST /v2/bot/message/pushfor the greeting, warning, end and timeout messages as well as the department flex message. - Works together with Live Chat Callback Handling, which receives events coming back from Mbox.