> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ada.cx/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ada.cx/_mcp/server.

# Handoff reasons

## Overview

When the AI Agent hands a conversation to a human agent through a [Handoff](/docs/handoffs), it records why. Use these reasons to find handoffs that you can reduce, such as a failing [Tool](/docs/automation/tools) or a gap in your [Knowledge](/docs/knowledge).

Each reason has two fields:

* `reason_code`: one value from a fixed list of ten, or `null` when no code was recorded. Use it to count, filter and compare handoffs.
* `reasoning`: an explanation, based on the conversation, the Tool results or the configuration of your AI Agent. Use it to understand a single handoff. It is `null` when no text was recorded.

The [Data Export API](/data-export-conversation-object) returns both fields in the `handoffs` array. The [`get_conversation`](/mcp/tools/get-conversation) MCP tool returns both fields, and [`get_available_filters`](/mcp/tools/get-available-filters) offers the `HANDOFFREASON` filter. See [Where the fields appear](#where-the-fields-appear).

## Limitations

[Handoff](/docs/handoffs) reasons have the following constraints:

* The AI Agent records reason codes from 2026-10-07. Most earlier handoffs have a `null` reason code.
* Some AI Agents do not choose a cause during a conversation. Their handoffs outside Playbooks record `unclassified`. These handoffs do not get a cause later.
* The Data Export `handoffs` array includes only the handoffs that the AI Agent starts. The array can be empty for an escalated conversation. Use `is_escalated` to know whether a conversation was escalated.
* The AI Agent writes the `reasoning` text in English. When it chooses one of the eight causes, the system asks for one or two sentences. Other text can be longer. Older text can be in the language of the conversation. Text that you set on a Playbook step can be in any language.

## Use cases

Reason codes show where to start when you want fewer [Handoffs](/docs/handoffs).

* **Fix failing [Tools](/docs/automation/tools)**: `tool_failed` handoffs can point to a Tool or an integration that failed or timed out.
* **Review Tool access**: `tool_unavailable` handoffs show tasks that need a Tool the AI Agent does not have or cannot use.
* **Fill [Knowledge](/docs/knowledge) gaps**: `knowledge_unavailable` handoffs show information that the AI Agent did not have. Check whether your Knowledge needs more content.
* **Review your routing**: `handoff_configured` handoffs show requests that your configuration sends to a human agent. Check whether the AI Agent could handle those requests.
* **Track requests for a person**: `end_user_requested` handoffs show where the end user's request for a human agent caused the handoff.

## Reason codes

The first eight codes are causes. When the AI Agent chooses a cause for a [Handoff](/docs/handoffs) during a conversation, it records the first cause in the table that applies. Only the system records `system_failed` and `unclassified`. A [Playbook](/docs/automation/playbooks) handoff step gets its reason in a different way, and its rules can differ from this table. See [How the reason is assigned](#how-the-reason-is-assigned).

| Code                    | Meaning                                                                                                                                                                                                                                              | Example `reasoning`                                                                                                                                                                           |
| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool_failed`           | A [Tool](/docs/automation/tools), an integration, a credential or a connected platform failed, for example with a timeout or a rejected credential. The code stays `tool_failed` when the end user then asks for a human agent.                      | The order lookup returned a timeout twice, so the Agent could not confirm the shipping status.                                                                                                |
| `end_user_requested`    | The end user asked for a human agent before the AI Agent offered one. Frustration alone does not count. Acceptance of the AI Agent's offer does not count. A request for a task, such as closing an account, is not a request for a human agent.     | The end user asked to speak to a person in their first message and repeated the request after the Agent offered help.                                                                         |
| `out_of_scope`          | A policy does not let the AI Agent handle the request, so a human agent must handle it. A new Tool would not change this.                                                                                                                            | The end user asked for a credit limit increase. Company policy requires a human agent to approve it.                                                                                          |
| `tool_unavailable`      | The AI Agent may handle the request, but none of its Tools can do a necessary task. The Tool does not exist, or your configuration does not give the AI Agent access to it.                                                                          | The end user cannot sign in because both the email address and the password are unknown. The self-service reset requires the existing email address, so account recovery needs a human agent. |
| `knowledge_unavailable` | The AI Agent does not have the information it needs. A search that finds nothing useful counts, and so does [Knowledge](/docs/knowledge) that does not cover the topic.                                                                              | The end user asked about the warranty on a discontinued model. The knowledge base has no article that covers it.                                                                              |
| `issue_unresolved`      | A relevant fix ran correctly, or the end user tried it, but the issue continued. The Knowledge and the Tools were adequate.                                                                                                                          | The end user tried all the documented APN and connection steps. Mobile internet still does not work after activation.                                                                         |
| `handoff_configured`    | The AI Agent could handle the request, but your configuration, such as a [Playbook](/docs/automation/playbooks) or a [Process](/docs/automation/processes), sends it to a human agent. The AI Agent uses this code only when no cause above applies. | The refund lookup ran. The Playbook sends overdue refunds to a human agent.                                                                                                                   |
| `other`                 | None of the seven causes above describes the handoff. The `reasoning` text states what did not fit.                                                                                                                                                  | The end user's messages stayed unclear after two requests to rephrase, so the Agent could not identify the request.                                                                           |
| `system_failed`         | The handoff was the fallback for an error in Ada. An error from one of your Tools is `tool_failed`, not `system_failed`.                                                                                                                             | Often `null`.                                                                                                                                                                                 |
| `unclassified`          | The system did not assign one of the eight causes. For example, a Playbook handoff step ran before the system assigned its reason.                                                                                                                   | Often `null`.                                                                                                                                                                                 |
| `null`                  | No reason code was recorded. It is different from `unclassified`, which is a recorded value.                                                                                                                                                         | Often `null`.                                                                                                                                                                                 |

A handoff has a `null` reason code in these cases:

* The handoff happened before 2026-10-07, when most handoffs had no recorded reason code.
* The AI Agent did not give a valid reason code.
* The AI Agent had to hand off, but no Handoff was available, so it sent an error message instead.

A handoff with a `null` reason code matches no `IS` value of the `HANDOFFREASON` filter. A count by reason code does not include these handoffs.

## How the reason is assigned

A [Handoff](/docs/handoffs) gets its reason at one of two moments, depending on how it was triggered.

* **During a conversation.** When the AI Agent decides to hand off, it records the `reason_code` and the `reasoning`. When the AI Agent chooses one of the eight causes, the system is instructed not to include personal data. Personal data includes names, addresses, account numbers and card numbers.
* **Playbook handoff steps.** Each handoff step in a [Playbook](/docs/automation/playbooks) can hold its own reason, and handoffs from the step record it.
  * You can set a step's `reason_code` and `reasoning` with the [`edit_agent_behavior`](/mcp/tools/edit-agent-behavior) MCP tool or the [Playbooks API](/reference/playbooks/overview). Set them on the `run` step that targets the Handoff. An MCP edit goes live when you promote its change set.
  * The `reason_code` must be one of the eight causes. The `reasoning` needs a `reason_code` and has a maximum of 300 characters.
  * Later saves keep the values that you set, including saves in the dashboard, which does not show these fields. To let the system assign the reason instead, set `reason_code` to `null`.
  * After you save a Playbook, the system assigns a reason to each handoff step that has none. It bases the reason on the steps that lead to the handoff step. Until then, or if that fails, handoffs from the step record `unclassified`. The system does not replace a reason that you set.
  * To read the reasons, use [`list_entities`](/mcp/tools/list-entities) for one Playbook or the Playbooks API. These reads show the reasons that you set, not the reasons that the system assigns. A [Snapshot API](/reference/snapshot-api/overview) export and import also keep the reasons that you set.

## Where the fields appear

Each location shows the [Handoff](/docs/handoffs) reason fields in its own format.

| Location                                   | Fields                                           | Notes                                                                                                                                                                                                                                                                                                                  |
| :----------------------------------------- | :----------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Data Export API, v2 Conversations endpoint | `handoffs[].reason_code`, `handoffs[].reasoning` | `reasoning` is a string, or `null` when no text was recorded. Each entry is the latest attempt for one handoff target, in time order. The export applies your [redaction rules](/docs/setup/redactions) to `reasoning`, as it does to message bodies. See the [Conversation object](/data-export-conversation-object). |
| MCP `get_conversation`                     | `reason_code`, `reasoning.text`                  | Returned on the `handoff_triggered` transcript entry. When text exists, `reasoning` is an object, and its `text` field holds the explanation. Otherwise, `reasoning` is `null`. `get_conversation` applies your redaction rules to the `reasoning` text. See [`get_conversation`](/mcp/tools/get-conversation).        |
| MCP `get_available_filters`                | `HANDOFFREASON`                                  | Filter conversations by reason code with `IS` or `ISNOT`. `ISNOT` also returns conversations with no handoff. See [`get_available_filters`](/mcp/tools/get-available-filters).                                                                                                                                         |

### Handoff attempts and escalations

A handoff reason belongs to one attempt of a [Handoff](/docs/handoffs). The AI Agent records the reason when it starts the attempt, even if the attempt fails. So a reason does not show that the end user reached a human agent.

* In the Data Export, each `handoffs` entry is the latest attempt for one handoff target. Its `outcome_status` gives the result, for example `succeeded` or `failed`.
* With `IS`, the `HANDOFFREASON` filter also matches conversations where the attempt with that reason failed.
* To find escalated conversations, use `is_escalated` in the Data Export or the `HANDOFF` filter in MCP. They show whether the conversation was escalated, not which handoff attempts the AI Agent made.

---

Have any questions? Contact your Ada team, or email us at [](mailto:help@ada.cx?subject=Help%20Docs%20inquiry).