Skip to content

Chat handover (human takeover)

Hand a live chat session from the agent to a human operator, in the same conversation the user is already in. The agent goes quiet, the user sees a short placeholder, an operator types directly into the chat from the admin, and on release the agent resumes — with the operator's messages in its context.

This is a complement to Human-in-the-loop workflows and escalate_to_human, not a replacement. Those pause a workflow or file an offline case; handover rescues a live conversation in place — a customer-facing chat that hits an edge case, a sales conversation that needs a person past a threshold, a regulated flow that legally requires a human.

Web chat only

Handover currently covers web chat sessions. Taking over SMS / WhatsApp conversations is planned for a later release.

How it works

  1. The agent calls the request_handover tool (or an operator barges in from the admin). The session enters requested.
  2. The user's chat keeps streaming, but instead of an agent answer it shows your configured placeholder ("A team member will respond shortly.").
  3. Martha notifies your configured channels and emits a chat.session.handover_requested event.
  4. An operator opens the session in the admin, clicks Take over (requestedactive), and types. Their messages appear in the user's chat live.
  5. The operator clicks Release. The session returns to none and the next user message routes back to the agent, which now sees the human exchange in its history.

If nobody picks up, or an operator claims and then goes idle, the session auto-releases after a configurable timeout so the conversation is never stuck.

Enable it for a tenant

Handover is off by default. Configure it with the settings endpoint:

http
PUT /api/admin/handover/settings
{
  "enabled": true,
  "fallback_message": "A team member will respond shortly.",
  "notify_channel_ids": ["<notification-connection-id>"],
  "timeout_minutes": 15
}
FieldMeaning
enabledMaster switch for the tenant. When off, sessions can never enter handover.
fallback_messageWhat the user sees while waiting for / talking to an operator.
notify_channel_idsNotification connection IDs to ping when a handover is requested (see below).
timeout_minutesOperator-inactivity auto-release window. Default 15; 0 disables auto-release.

GET /api/admin/handover/settings returns the current policy.

Get operators notified

notify_channel_ids are the IDs of notification channel connections. When a handover is requested, Martha sends a message to each — with the session reason and a deep link to the operator seat — so operators do not have to watch the sessions tab.

Use Slack or an authenticated webhook

Notification delivery resolves a stored credential, so the connection must carry one: a Slack connection (the webhook URL is the secret) or a webhook connection configured with an auth value. A no-auth webhook has no stored credential and will not deliver.

You can also react to the lifecycle events yourself — see Custom escalation.

Operate a handover (admin)

In the admin Sessions area:

  • The session list shows a state badge (Handover requested / active) and a filter by state, and the Sessions nav shows a count of pending handovers.
  • Open a session to reach the operator seat: Take over / Claim, a composer to type into the conversation, and Release. The banner shows the reason the agent (or workflow) gave.
  • The operator's name is resolved from the identity provider — the seat shows a person's name, not an internal id.

Who may operate a chat is controlled by the chat_session / claim grant, managed through the standard access surfaces (the same grant model as approvals). A session operator does not need to be a full tenant admin.

Let the agent ask for a human

Grant the request_handover tool to the agent (or API client) that runs the chat, the same way you grant any other tool. Then the agent can hand off on its own when it should not proceed — a request beyond its authority, a sensitive or regulated situation, or the user explicitly asking for a person. If handover is not enabled for the tenant, the tool returns a graceful error and the agent falls back to answering or to escalate_to_human.

Per-client control

Whether a client's sessions may enter handover at all is a per-client setting (Clients → edit → Human handover):

SettingBehavior
Auto (default)Allowed for internal clients; blocked for public embed clients.
AllowedHandover permitted for this client.
BlockedHandover refused for this client.

The Auto default means a real person never appears unexpectedly inside a public embedded chat widget unless you deliberately allow it.

Auto-release

An active handover with no operator activity for timeout_minutes is released automatically, and the agent resumes with the full conversation. The clock resets on every operator message, so an actively-typing operator is never cut off. Set timeout_minutes to 0 to disable auto-release entirely.

SMS and WhatsApp sessions

Handover works on messaging channels, not just the web chat. When an SMS or WhatsApp session enters handover, the same operator seat applies — with two differences that follow from the channel:

  • One placeholder, not one per message. When the handover starts, the tenant's fallback_message is texted to the user once. Further messages the user sends while waiting are received and shown to the operator live, but the user is not re-texted each time (unlike the web widget, where every message gets an ack) — SMS is billed per segment.
  • The agent stops texting. While a handover is requested or active, the agent neither runs nor sends anything on that channel. If an operator takes over while the agent is mid-reply, that in-flight reply is suppressed too.

In the operator seat, the composer shows the destination channel and phone number so it is clear the reply goes out over SMS or WhatsApp — from the session's own business number, the one the user has been messaging. Replies are delivered through your messaging provider; if a delivery fails the operator sees an inline notice (the message is still recorded in the conversation).

WhatsApp 24-hour window. WhatsApp only allows free-form business messages within 24 hours of the user's last message. During a live handover that window is normally open. If an operator replies after it has closed, delivery falls back to a pre-approved reopen template. Configure it in handover settings:

SettingMeaning
whatsapp_reopen_template_nameName of a template pre-approved in your WhatsApp provider account, with a single body placeholder that carries the operator's text.
whatsapp_reopen_template_languageTemplate language code (default en).

If no reopen template is configured and the window has closed, the operator's message is recorded but delivery fails with a clear notice — no template name is guessed, because the provider requires per-account pre-approval.

Settings are managed today through the handover settings API; a unified settings form covering the web and messaging options together is tracked as a follow-up (#977), sequenced after this so one form covers both.

Custom escalation

Every transition emits an event on the tenant event bus:

EventWhen
chat.session.handover_requestedA handover is requested (by the agent, an operator, or a workflow).
chat.session.handover_claimedAn operator takes the session.
chat.session.handover_releasedThe session is released — by the operator or by inactivity auto-release (data.reason distinguishes them).

The built-in notify_channel_ids fan-out is the out-of-the-box ping. For richer policy — urgency-based routing, on-call paging, a CRM update on release — wire an event-driven trigger to a workflow that reacts to chat.session.handover_requested. The admin takeover and your escalation workflow never call each other; they meet only on these events.

Martha is built by aiaiai-pt.