> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usefini.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fini for escalation and human handoff

> How Fini decides to escalate, tags every escalation with a reason, hands the conversation to your team on each surface with the transcript and trace attached, and how to tune it.

export const ScenarioChecker = ({title = "Try it", question, scenarios = [], labels = {
  yes: "Yes",
  no: "No",
  depends: "It depends"
}}) => {
  const FV = {
    lime: "#C3EE5E",
    ink: "#131415",
    line: "rgba(127,127,127,0.28)",
    soft: "rgba(127,127,127,0.07)",
    softer: "rgba(127,127,127,0.04)",
    muted: "rgba(127,127,127,0.95)",
    pass: "#C3EE5E",
    warn: "#FFB020",
    fail: "#FF4D4D",
    radius: 14
  };
  const fvCard = {
    border: `1px solid ${FV.line}`,
    borderRadius: FV.radius,
    padding: 18,
    margin: "20px 0",
    background: FV.softer
  };
  const fvChip = active => ({
    border: `1px solid ${active ? FV.lime : FV.line}`,
    background: active ? FV.lime : "transparent",
    color: active ? FV.ink : "inherit",
    borderRadius: 999,
    padding: "6px 12px",
    fontSize: 13,
    fontWeight: 600,
    cursor: "pointer",
    lineHeight: 1.2
  });
  const fvBtn = primary => ({
    border: `1px solid ${primary ? FV.lime : FV.line}`,
    background: primary ? FV.lime : "transparent",
    color: primary ? FV.ink : "inherit",
    borderRadius: 10,
    padding: "7px 14px",
    fontSize: 13,
    fontWeight: 600,
    cursor: "pointer"
  });
  const fvLabel = {
    fontSize: 11,
    fontWeight: 700,
    letterSpacing: "0.08em",
    textTransform: "uppercase",
    opacity: 0.6,
    marginBottom: 8
  };
  const [k, setK] = useState(null);
  const s = k === null ? null : scenarios[k];
  const col = {
    yes: FV.pass,
    no: FV.fail,
    depends: FV.warn
  };
  return <div style={fvCard}>
      <div style={fvLabel}>{title}</div>
      {question && <div style={{
    fontSize: 16,
    fontWeight: 700,
    marginBottom: 12
  }}>{question}</div>}
      <div style={{
    display: "grid",
    gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
    gap: 8
  }}>
        {scenarios.map((sc, i) => <button key={i} onClick={() => setK(i)} style={{
    textAlign: "left",
    padding: "10px 12px",
    borderRadius: 10,
    cursor: "pointer",
    fontSize: 13.5,
    lineHeight: 1.4,
    color: "inherit",
    border: `1px solid ${k === i ? FV.lime : FV.line}`,
    background: k === i ? "rgba(195,238,94,0.14)" : "transparent"
  }}>{sc.label}</button>)}
      </div>
      <div style={{
    marginTop: 14,
    minHeight: 64,
    padding: "12px 14px",
    borderRadius: 10,
    border: `1px solid ${s ? col[s.verdict] : FV.line}`,
    background: FV.soft,
    transition: "border-color .25s"
  }}>
        {s ? <div>
            <b>{labels[s.verdict]}</b>{s.title ? <b>{`: ${s.title}`}</b> : null}
            <div style={{
    fontSize: 14,
    marginTop: 4,
    lineHeight: 1.55
  }}>{s.why}</div>
          </div> : <span style={{
    fontSize: 13.5,
    opacity: 0.6
  }}>Pick a scenario to see the answer.</span>}
      </div>
    </div>;
};

Fini (usefini.com) escalates a conversation to your team when your rules say it should (Escalation Topics in the Planning Prompt, handoff Replies in Intent Rule policy branches, articles whose **Escalation** field is set to **Yes**, Guardrails) or when it can't resolve the request (missing knowledge, a failed Action, or a customer who asks for a person), and it tags every escalation with a reason from a fixed taxonomy you can report on in Analytics. The handoff lands where your team already works: in the connected helpdesk for helpdesk conversations, as a ticket created by a Business Rule for widget conversations, or in Inbox native ticketing, with the transcript, customer attributes and the AI Steps trace available to the person who picks it up.

Escalation is configured, not left to chance. You decide the triggers, the destination and what the customer is told, and you can audit every escalation afterwards.

## What Fini handles and what it hands to your team

| Fini handles | Your team handles |
| - | - |
| Deciding to escalate according to your Escalation Topics, rules and guardrails. | The conversation from the moment it's escalated. |
| Telling the customer what happens next, in your wording. | Resolving the request, and any decision Fini isn't allowed to make. |
| Creating the ticket in your helpdesk for widget escalations (Business Rules), or routing a native ticket to an Agent group. | Ticket ownership, queues and SLAs in your helpdesk or Inbox. |
| Marking the conversation's status in the helpdesk so your views and automations can pick it up. | Feeding fixes back: knowledge gaps, missing Actions, rules that are too strict. |
| Staying silent once a human is assigned, if you configure it to. | |
| Tagging the escalation reason for Analytics. | |

## How Fini decides to escalate

Several layers can cause an escalation. Each one is configured separately, so you can fix the right layer when escalations land for the wrong reason.

| Trigger | What it is | Where you configure it |
| - | - | - |
| **Escalation Topics** | Plain-language triggers in the Planning Prompt. The planner routes matching conversations to a human and skips knowledge search. Defaults cover legal or regulatory issues and persistent human requests (`agent_request_count >= 3`, `issue_repeat_count >= 3`); you add business-specific patterns. | [Prompts](/en/configuration/prompts) |
| **Customer asks for a person** | Recorded as one of the two **Customer requested human** reasons, depending on whether it happened on the first message or after the agent tried. | Planning Prompt (how persistent a request must be) |
| **Knowledge can't answer** | No content, conflicting content, or only partial content for the question. | [Knowledge](/en/knowledge/overview) |
| **An Action is missing or fails** | The agent needed a system that isn't connected, or the API call failed at runtime. | [Actions](/en/api-reference/actions) |
| **A rule's policy path** | A branch of an Intent Rule that, by your design, hands off instead of acting (for example a refund above your limit). The branch ends in a handoff **Reply** that tells the customer a teammate will follow up. | [Intent Rules](/en/automations/rulebook) |
| **An article marked for escalation** | The article-level **Escalation** field, a yes/no dropdown in the article editor. Set to **Yes**, hitting the article triggers an escalation path, useful for topics that should always loop in a human. | [Articles](/en/knowledge/articles) |
| **Guardrails** | A reply fails a check, the one rewrite also fails, and Fini replaces the reply with a handoff message and marks the conversation for escalation. | [Guardrails](/en/configuration/guardrails) |
| **The model fails** | On supported integration channels, an unrecoverable model failure during planning, knowledge search, input-tag selection or answer generation becomes an internal failure note plus a human escalation, instead of a failure message sent to the customer. Slack has no transfer path for this, so Fini records a no-reply outcome there. | Automatic, see [Reply Rules](/en/automations/reply-behavior) |

```mermaid theme={null}
---
title: How Fini decides to escalate
---
flowchart TD
    MSG(["Customer message"]) --> ET{"Matches an<br/>Escalation Topic?"}
    ET -->|"Yes, skips<br/>knowledge search"| ESC
    ET -->|"No"| WORK["Agent works the request<br/>Intent Rule or Knowledge"]
    WORK --> CAN{"Can it resolve<br/>the request?"}
    CAN -->|"Customer asks<br/>for a person"| ESC
    CAN -->|"Knowledge<br/>can't answer"| ESC
    CAN -->|"Action missing<br/>or fails"| ESC
    CAN -->|"Rule's handoff Reply<br/>or article Escalation"| ESC
    CAN -->|"Yes"| GR{"Guardrails<br/>check the reply"}
    GR -->|"Pass"| OUT(["Reply delivered"])
    GR -->|"Fail"| RW["One rewrite"]
    RW -->|"Pass"| OUT
    RW -->|"Fails again"| ESC
    ESC(["Handoff to your team<br/>Escalated to Human Agent<br/>reason tagged"])

    classDef source fill:#F7F7F7,color:#131415,stroke:#E8E8E8
    classDef agent fill:#131415,color:#FFFFFF,stroke:#131415,stroke-width:3px
    classDef surface fill:#FFFFFF,color:#131415,stroke:#131415
    classDef human fill:#C3EE5E,color:#131415,stroke:#131415,stroke-width:2px

    class MSG,OUT source
    class WORK agent
    class ET,CAN,GR,RW surface
    class ESC human
```

Whatever the trigger, the conversation's [Conversation Status](/en/configuration/tags#mandatory-groups-conversation-status) becomes **Escalated to Human Agent** when the agent hands off or says the request was passed to a team that will follow up. That status drives the Inbox badge, the helpdesk status markers and Analytics.

Inside an Intent Rule, hand off with a **Reply** in the branch that tells the customer a teammate will follow up (for example, "Your request is with our team, who will follow up by email"). That is the pattern the [walkthroughs](/en/walkthroughs/cancellation-flow) and API examples use. Outside a rule, the article-level **Escalation** field and *Escalation Topics* in the Planning Prompt hand off, and for widget conversations a Business Rule with the **On Escalation** trigger creates the ticket in your helpdesk.

## Escalation reasons

Every escalated conversation is tagged with a reason from the **Escalation Reason** tag group. Analytics groups them into four families.

| Family | Reason | Usually means | First fix to try |
| - | - | - | - |
| Knowledge | **Missing Knowledge** | No content covered the question. | Add an article or source; check [Review](/en/knowledge/review) for proposed articles. |
| Knowledge | **Conflicting Knowledge** | Two sources disagreed. | Pick the authoritative source and retire the other. |
| Knowledge | **Partially Available** | Some of the answer existed. | Complete the article, or add a rule for the partial case. |
| Action | **Missing API Access** | A needed system isn't connected. | Build the [Action](/en/api-reference/actions). |
| Action | **API or System Failure** | An Action or integration call failed. | Read the Tool node in the AI Steps trace, then check the API. |
| User | **Customer requested human** (immediately) | The customer asked for a person on the first message. | Often unavoidable; spikes can point at the welcome message. |
| User | **Customer requested human** (after attempt) | The customer tried the agent, then asked for a person. | The most actionable reason. Read these conversations. |
| User | **Ambiguous or Unclear Input** | The agent couldn't understand well enough to act. | Tighten intent gates, or add a clarifying-question branch. |
| Policy | **Escalation Constraint** | A policy in your Rulebook stopped the agent from acting. | Expected; audit if a category is unexpectedly high. |
| Policy | **Guardrail or Safety Trigger** | A safety check forced the handoff. | Expected; check for false positives. |

The full definitions live in [Analytics → Escalation reasons](/en/analytics#escalation-reasons).

<ScenarioChecker
  title="Try it: name the escalation reason"
  question="Which reason does this escalation get, and does it need a fix?"
  labels={{ yes: "Fix it", no: "Expected", depends: "Watch for spikes" }}
  scenarios={[
{ label: "A customer asks about a fee that no article mentions.", verdict: "yes", title: "Missing Knowledge (Knowledge)", why: "No content covered the question. Add an article or source, and check Review for proposed articles." },
{ label: "Two help center articles give different answers to the same question.", verdict: "yes", title: "Conflicting Knowledge (Knowledge)", why: "Two sources disagreed. Pick the authoritative source and retire the other." },
{ label: "An article covers part of the answer, but not the case the customer is in.", verdict: "yes", title: "Partially Available (Knowledge)", why: "Some of the answer existed. Complete the article, or add a rule for the partial case." },
{ label: "The customer asks about their order, and no order system is connected.", verdict: "yes", title: "Missing API Access (Action)", why: "A needed system isn't connected. Build the Action." },
{ label: "The Action the rule calls returns an error at runtime.", verdict: "yes", title: "API or System Failure (Action)", why: "An Action or integration call failed. Read the Tool node in the AI Steps trace, then check the API." },
{ label: "The customer's first message asks for a person.", verdict: "depends", title: "Customer requested human, immediately (User)", why: "Often unavoidable. Spikes can point at the welcome message." },
{ label: "The customer tries the agent for a few turns, then asks for a person.", verdict: "yes", title: "Customer requested human, after attempt (User)", why: "The most actionable reason. Read these conversations to see where the agent lost the customer." },
{ label: "The message is too unclear for the agent to act on.", verdict: "yes", title: "Ambiguous or Unclear Input (User)", why: "Tighten intent gates, or add a clarifying-question branch." },
{ label: "A refund above your auto-approve limit hands off, as your rule intends.", verdict: "no", title: "Escalation Constraint (Policy)", why: "A policy in your Rulebook stopped the agent from acting. Expected; audit if a category is unexpectedly high." },
{ label: "A reply fails a guardrail check and the rewrite fails too.", verdict: "no", title: "Guardrail or Safety Trigger (Policy)", why: "A safety check forced the handoff. Expected; check for false positives." }
]}
/>

## How the handoff works on each surface

| Surface | Where the human picks it up | How Fini hands off | Configure |
| - | - | - | - |
| **Helpdesk integrations** (Zendesk, Intercom, Front, HubSpot, Salesforce, Gorgias, LiveChat) | The same ticket or conversation in your helpdesk. | Fini marks the conversation's status in the helpdesk: tags such as `fini_escalated_human_agent` in Intercom, Front, Gorgias and LiveChat; conversation-status properties in HubSpot; a custom escalation ticket field in Zendesk; the **Fini Transfer** field in Salesforce. Your helpdesk views and assignment rules take it from there. | The integration's page under [Deploy](/en/deploy/overview) |
| **Widget, escalating into a helpdesk** | A new ticket in Zendesk, Front, Salesforce, HubSpot or Gorgias. | A published [Business Rule](/en/automations/business-rules) with source **Widget** and trigger **On Escalation** creates the ticket, can post a final message to the customer, and writes the external ticket ID or URL back to the conversation. | **Rulebook → Business Rules** |
| **Widget and native email, handled in Fini** | Inbox native ticketing. | The escalated conversation becomes a native ticket and is routed to an [Agent group](/en/configuration/agent-groups) by tag (tags on the escalating turn first, then earlier conversation tags), then assigned by **Round robin** or **Least loaded**. A human reply pauses the agent. | [Agent groups](/en/configuration/agent-groups), [Inbox](/en/testing/inbox#native-ticketing) |
| **Slack** | The Slack channel or DM. | The agent escalates to teammates when needed; [Reply Rules](/en/automations/reply-behavior) decide whether it replies, leaves an internal note, or stays silent. | [Slack](/en/deploy/slack) |
| **Voice** | Your human phone queue, or a ticket. | The voice agent escalates as a live transfer to your phone queue or as a ticket, and the transcript is handed over with it. The call appears in Inbox with the same AI Steps trace. Inbound routing is set up with your Fini contact. | **Voice** in the agent sidebar, see [Deploy](/en/deploy/overview) |

<Note>
  If no Business Rule applies to a widget escalation, no destination ticket is created by the Business Rules runtime. Every agent that serves the widget and escalates into a helpdesk needs an assigned, published Business Rule.
</Note>

### Example widget escalation Business Rule

Start from a default template (**Use Default Rule**) when it matches your helpdesk. Use **Create Custom** when routing depends on who the customer is. The tree below is an example to adapt; the ticket-creation Tool is an Action you configure against your helpdesk's API, and the field names are placeholders.

```text theme={null}
Steps  (Business Rule: Source Widget, Trigger On Escalation)
├── Fallback
│   ├── Steps  (priority lane)
│   │   ├── Check: plan Equals enterprise
│   │   └── Tool:  Create Helpdesk Ticket
│   │         in:  transcript, interaction_id, user_attributes,
│   │              priority = urgent, queue = priority_support
│   │         out: ticket_id, ticket_url
│   └── Tool:  Create Helpdesk Ticket
│         in:  transcript, interaction_id, user_attributes,
│              priority = normal, queue = general
│         out: ticket_id, ticket_url
└── Send Message: tell the customer a teammate has the conversation,
                  and share ticket_id
```

Map the transcript, interaction ID and attributes from the Business Rule context fields (**Interaction History Transcript**, **Interaction ID**, **Formatted User Attributes**), and use **Test** on the rule card before relying on it.

## What context the human receives

The goal is that your teammate never has to ask the customer to repeat themselves.

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor C as Customer
    participant F as Fini
    participant BR as Business Rule<br/>On Escalation
    participant H as Your helpdesk<br/>via an Action
    actor T as Your teammate

    C->>F: A request Fini should hand off
    Note over F: Escalation decided, reason tagged
    F->>BR: Widget conversation escalates
    BR->>H: Create Helpdesk Ticket
    Note over BR,H: Transcript, Interaction ID and user attributes mapped in
    H-->>BR: ticket_id, ticket_url
    BR-->>F: External ticket ID written back to the conversation
    BR->>C: Final message that a teammate has it, with ticket_id
    T->>H: Picks up the ticket with transcript and attributes
    T->>F: Opens the conversation in Inbox for the AI Steps trace
    T->>C: Replies without asking the customer to repeat themselves
```

| Context | Where it comes from |
| - | - |
| The full conversation | The helpdesk thread itself, or **Interaction History Transcript** mapped into the ticket by a Business Rule. |
| Who the customer is | User Attributes, mapped with **Formatted User Attributes** in Business Rules, or visible on the conversation in Inbox. |
| A link back to the Fini conversation | **Interaction ID** in Business Rules; HubSpot support-form handoffs require Fini's interaction ID property for this reason. |
| Files the customer uploaded | On Zendesk chat escalations from the widget, uploads are forwarded as native attachments when Zendesk accepts the file; the transcript includes download links as a fallback. |
| Why the agent escalated, and what it tried | The AI Steps trace in [Inbox](/en/testing/inbox): planning, the rule that ran, Tool inputs and outputs, guardrail verdicts and the tags applied, including the escalation reason. |

## Guardrails and escalation settings to configure

| Goal | Configuration |
| - | - |
| Never talk over a human | **No Reply** with `Human Agent Assigned Equals True`; on Zendesk, add a group with `Escalated Conversation Equals True` (a documented example in [Reply Rules](/en/automations/reply-behavior#example-rules)). |
| Hand specific topics to people | Add them to *Escalation Topics* in the Planning Prompt. Be specific about the pattern: *"escalate when the customer has explicitly asked for a human three or more times"* works; *"escalate frustrated customers"* doesn't. |
| Keep rule-backed topics with the rule | If a topic has an Intent Rule (cancel, refund, account update), let the rule handle it rather than listing the topic as an escalation trigger. |
| Draft but don't send for sensitive intents | **Internal Comment** for the intent's tag, so a human reviews and sends. |
| Say the right thing at handoff | Put exact escalation wording in **Predefined Replies** in Main Guidelines, and the widget's final message in the Business Rule. |
| Stop unsafe replies | [Guardrails](/en/configuration/guardrails). A failed check gets one rewrite; if that fails, the customer receives a handoff message and the conversation is marked for escalation. |

## How to tune escalation

1. **Read the escalation reasons weekly.** In [Analytics](/en/analytics), the escalation doughnut and the **Escalation reason** filter tell you which family dominates. Knowledge reasons are fixed in Knowledge, Action reasons in Actions, User reasons by reading conversations, Policy reasons by auditing your own rules.
2. **Start with "after attempt".** Filter to the after-attempt **Customer requested human** reason and read ten conversations. These show where the agent was close but lost the customer.
3. **Check which subsection fired.** For an escalation you didn't expect, open the conversation in [Inbox](/en/testing/inbox) and read the AI Steps panel, which shows the Planning Prompt reasoning. Edit the subsection responsible.
4. **Audit guardrail escalations.** In [Guardrails](/en/configuration/guardrails), filter to **Firing** policies and review verdicts. A hit counts the initial failed check, including replies that were rewritten successfully, so hits are not the same as escalations.
5. **Lock fixes in.** Turn the conversations you fixed into [Test Suite](/en/testing/test-suite) test cases, with an exact check for the expected reply or handoff, so a later change doesn't bring the escalation back.

## What to measure

| Metric | Where | What it tells you |
| - | - | - |
| Human escalation rate | KPI card in [Analytics](/en/analytics) | The share of conversations your team receives. Deflection rate is its complement and also counts conversations waiting on the customer. |
| AI resolution rate | **Conversation Status** doughnut, share **Resolved by AI** | The share the agent fully resolved, the counterpart to escalation. |
| Escalation reasons | Escalation doughnut and the **Escalation reason** filter | What to fix next, by family. |
| Escalated Rate per intent or rule | **Intent rule breakdown** and **Knowledge performance** tables | Which intents and knowledge areas leak to humans. |
| Hourly resolution | **Hourly breakdown** (ranges up to 31 days) | Hours where resolution rate falls off, often a staffing or routing gap. |
| CSAT | **Average CSAT**, with the **Conversation status** filter set to **Escalated to Human Team** | Whether escalated customers end satisfied. |

## Related

<CardGroup cols={2}>
  <Card title="Business Rules" icon="zap" href="/en/automations/business-rules">
    Widget escalation templates, custom escalation trees, field mapping and testing.
  </Card>

  <Card title="Cancellation flow" icon="route" href="/en/walkthroughs/cancellation-flow">
    Walkthrough that pairs an Intent Rule with an Internal Comment review gate for VIP customers.
  </Card>

  <Card title="Prompts" icon="message" href="/en/configuration/prompts">
    Escalation Topics in the Planning Prompt and Predefined Replies.
  </Card>

  <Card title="Agent groups" icon="users" href="/en/configuration/agent-groups">
    Route native tickets to human teams by tag, availability and workload.
  </Card>

  <Card title="Reply Rules" icon="turn-down-right" href="/en/automations/reply-behavior">
    No Reply, Internal Comment and Direct Reply conditions.
  </Card>

  <Card title="Analytics" icon="chart-bar" href="/en/analytics">
    Escalation reasons, escalation rate and resolution by intent.
  </Card>
</CardGroup>


## Related topics

- [Fini for password reset and account access](/en/use-cases/password-reset.md)
- [How Fini works](/en/how-fini-works.md)
- [Fini FAQ](/en/faq.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.