Agent to human handoff packet
Shared by Pavel · Updated 2026-11-06
Direct answer: Pass ten fields, not one: record id, what they asked, what the agent promised, facts with sources, what it was unsure of, next step with deadline, thread link, owner, and a human_took_over flag. n8n's own human-fallback example passes only the user's message.
Below: the n8n schema to copy, a Slack layout, three failure modes, and a comparison with Intercom and Zendesk.
Label key: [vendor] = vendor documentation; [measured] = measured by someone; [opinion] = judgment. All sources read 2026-11-06.
What n8n's human-fallback example passes [vendor]
Source: https://docs.n8n.io/advanced-ai/examples/human-fallback/ and its workflow file https://raw.githubusercontent.com/n8n-io/n8n-docs/refs/heads/main/docs/_workflows/advanced-ai/examples/ask_a_human.json
- The agent calls a tool named
dont_know_tool. The tool passes exactly one field:chatInput, set to{{ $('Chat Trigger').item.json.chatInput }}. - The sub-workflow posts to Slack:
A user had a question the bot couldn't answer. Here's their message:pluschatInput. - It checks
chatInputagainst an email regex. If none, it replies asking for an email. The email is not passed on as its own field. - Not in the example: record id, promises, sources, uncertainty, deadline, owner, thread link, takeover flag.
The packet
| Field | Type | Note |
|---|---|---|
record_id | string | the CRM or ticket id |
asked | string | the customer's request, their words |
promised | array of string | anything the agent said it would do |
facts | array of {claim, source} | each with a URL or record |
unsure | array of string | what the agent could not confirm |
next_step | string | one action |
deadline | string | ISO 8601 date-time |
thread_url | string | full conversation |
owner | string | the named human |
human_took_over | boolean | false until a human replies |
Copy-ready n8n inputs (not yet tested end to end)
Receiving workflow, Execute Sub-workflow Trigger. Set Input data mode to "Define using JSON example" and paste. Per n8n's node source, a null value allows any type [vendor, https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/ExecuteWorkflow/ExecuteWorkflowTrigger/ExecuteWorkflowTrigger.node.ts]:
{
"record_id": "a string",
"asked": "a string",
"promised": [],
"facts": [],
"unsure": [],
"next_step": "a string",
"deadline": "a string",
"thread_url": "a string",
"owner": "a string",
"human_took_over": false
}
Calling workflow, Execute Sub-workflow node. Source "Database", pick the workflow "From list"; the fields then appear to map [vendor, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.executeworkflow.md]. Map: record_id = {{ $json.record_id }}, and so on for each field. Turn on "Wait for Sub-Workflow Completion" if you need the reply.
Set node (JSON Output mode). The Edit Fields node offers a JSON Output mode [vendor, https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set.md]; in n8n's source the parameters are mode: "raw" and jsonOutput (https://github.com/n8n-io/n8n/blob/master/packages/nodes-base/nodes/Set/v2/raw.mode.ts):
{
"mode": "raw",
"jsonOutput": "={{ { record_id: $json.record_id, asked: $json.asked, promised: [], facts: [], unsure: [], next_step: '', deadline: '', thread_url: '', owner: '', human_took_over: false } }}"
}
Checked against docs: the mode and parameter names and the null-for-any-type rule. Not checked: running it in an n8n instance.
Slack message layout (not yet tested)
*Handoff: {{record_id}}* owner: {{owner}} due: {{deadline}}
*Asked:* {{asked}}
*We promised:* {{promised}}
*Facts:* {{claim}} ({{source}})
*Unsure:* {{unsure}}
*Next step:* {{next_step}}
Thread: {{thread_url}}
[opinion] Put promises above facts. The human most needs to know what the customer already believes.
Failure modes [opinion unless marked]
- Double replies after takeover. The agent and the human both answer. Set
human_took_overthe moment a human replies and check it before every agent send. Evidence this happens: a third-party guide says Intercom's Fin keeps replying until a teammate sends a customer-facing message (https://www.getmacha.com/blog/intercom-fin-handoff-setup, secondhand, not confirmed in Intercom's own article). - Lost promises. "I'll send the contract today" sits in the thread, not in a field. Extract promises into
promisedbefore handoff. - Stale CRM fields. The packet copies a field that changed since. Store the record id and read the live record at takeover; keep
factsas dated claims.
Public handoff docs compared
| Intercom (Fin) | Zendesk (AI agents) | |
|---|---|---|
| Context to human | Not stated in the article read | Messaging history is backfilled into the ticket; conversation metadata is mapped to the ticket at escalation |
| Why it escalated | An attribute records the reason | A conversation tag such as async_escalation in the recipe |
| After handoff | Not stated | Ticket becomes a regular, editable ticket |
| Source | https://www.intercom.com/help/en/articles/12396892-manage-fin-ai-agent-s-escalation-guidance-and-rules | https://support.zendesk.com/hc/en-us/articles/9204149016346 and https://support.zendesk.com/hc/en-us/articles/11285019603098 |
All cells [vendor]. Neither page describes promises, uncertainty or a deadline field. Intercom's article also describes an "Ask for input" rule that keeps the conversation with Fin while a teammate reviews a draft.
Gaps
- Intercom's article does not say what Fin passes to the teammate or whether Fin stops replying; I did not find the primary answer.
- I read no Intercom or Zendesk page on summaries; Zendesk's page does not mention an AI summary.
- Nothing here is measured. No source read reports how often double replies or lost promises occur.