Human handoff
Transfer a conversation to a human agent. Configurable conditions, business-hours schedule, offline message, and contact capture.
The Human Handoff tool (key: "handoff", module Chatbotgen.AI.Tools.Handoff) gives the chatbot a way to stop answering and put a real person in the loop.
Tool description (verbatim from seed)
Human Handoff — Transfer conversations to human agents when the AI can't help. Configure when and why handoffs happen.
Function definition (what the LLM sees)
Verified from handoff.ex:
-
Name:
request_human_handoff - Description: "Transfer the conversation to a human agent when you cannot help or the user requests one."
-
Parameters:
-
reason(string, required) — "Brief reason why human handoff is needed"
-
Configuration fields
Enabling the tool shows an inline form with 11 fields. Every field has a default from the seed, so saving without edits already gives you a working configuration.
Handoff Conditions (textarea)
- Label: "Handoff Conditions"
- Not required
- Default (verbatim):
Transfer the conversation to a human agent when:
- The user explicitly asks to speak with a human
- You cannot answer their question after trying
- The topic involves complaints, refunds, billing issues, or sensitive matters
- The user seems frustrated or dissatisfied with your answers
Do NOT transfer for simple questions you can answer from the knowledge base.
This is stored on config["prompt"] and injected into the system prompt for the conversation.
Offline Message (textarea)
- Label: "Offline Message"
- Not required
- Default: "Our team is currently offline. Please leave your message and we'll get back to you as soon as possible."
Stored on config["offline_message"]. Returned as the chatbot's reply when the tool fires outside business hours (see below).
Enable Schedule (checkbox)
- Label: "Enable Schedule"
-
Default:
"false"(stored as string because the UI serializes checkboxes as"true"/"false")
When disabled, the tool ignores business hours entirely — every call completes the handoff.
Available Days (text)
- Label: "Available Days (comma-separated, e.g. mon,tue,wed,thu,fri)"
-
Default:
mon,tue,wed,thu,fri
Compared at runtime against the current weekday using three-letter lowercase names (mon, tue, wed, thu, fri, sat, sun). The mapping comes from handoff.ex's day_name/1.
Start Time (text)
- Label: "Start Time (HH:MM, 24h)"
-
Default:
09:00
End Time (text)
- Label: "End Time (HH:MM, 24h)"
-
Default:
18:00
The within-schedule check is day in days and time >= start_time and time < end_time — end time is exclusive.
Timezone (text)
- Label: "Timezone (e.g. America/Bogota)"
-
Default:
America/Bogota
Use a standard IANA timezone identifier. If DateTime.now(tz) returns {:error, _} (unknown tz), the tool assumes agents are available.
Enabled Channels (text)
- Label: "Enabled Channels (comma-separated — widget,whatsapp,telegram). Leave empty for all."
-
Default:
widget,whatsapp,telegram
The channels credential is used by AI.ToolRunner to filter tools per conversation channel. Clearing this field enables the tool on all channels.
Collect visitor name/email before handoff (checkbox)
- Label: "Collect visitor name/email before handoff"
-
Default:
"true"
Stored on config["collect_contact"]. Any value other than the string "false" is treated as true.
Contact capture mode (text)
- Label: "Contact capture mode (required | optional)"
-
Default:
required
Passed through to the rich response as contact_mode.
Fields to collect (text)
- Label: "Fields to collect (comma-separated — name,email,phone)"
-
Default:
name,email
Split on ,, trimmed, empty strings removed. Passed through to the rich response as contact_fields.
Runtime flow (verified from handoff.ex's call/2)
When the LLM calls request_human_handoff:
-
The
reasonarg is pulled out (falls back to "User requested human assistance" if missing). - If schedule is enabled AND the current time is outside the schedule window, the tool returns ONLY the offline message as the chat reply — no structured handoff signal is emitted. The conversation stays in its current status.
-
Otherwise, the tool returns:
- Text reply: "I'm connecting you with a human agent. Someone from our team will be with you shortly."
-
Rich response:
type: "handoff" reason:collect_contact: config["collect_contact"] != "false" contact_mode: config["contact_mode"] || "required" contact_fields:
System prompt injection (verified from handoff.ex's system_context/1)
On every conversation, the tool adds the following to the system prompt:
You have a tool called "request_human_handoff" to transfer conversations to a human agent.
{Handoff Conditions}
{schedule note, if any}
The schedule note is:
- "Human agents are currently AVAILABLE." when the schedule is enabled and we're within hours
- "Human agents are currently OFFLINE. If the user requests a human, inform them agents are unavailable and offer to take a message." when the schedule is enabled and we're outside hours
- empty string when the schedule is disabled
Plan notes
Enabling tools at all requires the AI Tools feature. The Tools page's feature-lock card points to Growth — $79/mo. See Plans & pricing for the feature matrix.