Every automated workflow has an edge: the refund above the assistant's authority, the contract clause the extraction prompt wasn't designed for, the customer who is angry rather than confused. Good systems don't try to eliminate the edge — they make crossing it graceful. Graceful means three things: the model recognizes it should stop, the handoff carries everything a human needs to continue without re-asking, and the user experiences a warm transfer rather than a dead end.
Write the escalation policy into the prompt
Models default to being helpful, so "escalate when unsure" is too vague to act on. Anthropic's guidance to be clear and direct — and to explain why an instruction matters so Claude can generalize — applies with force here. Enumerate concrete triggers and give the reason:
"Escalate to a human agent when: the customer requests a refund over $500 (agents lack authority above this — approval is a finance decision); the customer mentions legal action or a regulator (these require the compliance team); you would need to state a policy you cannot find in the provided documents (guessing policy creates liability); or the customer asks for a person. When none of these apply, resolve the issue yourself."
Two details matter. The explanations let the model handle cases you didn't enumerate — a subpoena mention triggers the "regulator" reasoning even though the word "subpoena" appears nowhere. And the last sentence guards the opposite failure: over-escalation, where the model forwards everything and automates nothing. Both failure modes are real, and only your eval set tells you which way a given prompt leans.
Make escalation a tool, not a sentence
If escalation is just prose in a reply ("I'll transfer you now…"), your software has to detect it by parsing text — fragile by design. The robust pattern is a client tool: define escalate_to_human with a JSON Schema input, and when Claude decides to hand off, it returns stop_reason: "tool_use" and a structured payload your routing code executes directly.
escalation_tool = {
"name": "escalate_to_human",
"description": "Hand this conversation to a human agent. Use when "
"any escalation trigger in the system prompt is met.",
"strict": True, # guarantees schema-valid input
"input_schema": {
"type": "object", "additionalProperties": False,
"required": ["reason", "summary", "attempted", "urgency"],
"properties": {
"reason": {"type": "string", "enum": ["over_authority",
"legal_regulatory", "missing_policy",
"customer_request", "other"]},
"summary": {"type": "string"},
"attempted": {"type": "string"},
"urgency": {"type": "string", "enum": ["low", "normal", "high"]}}}}
Setting "strict": true on the tool definition guarantees the tool call matches your schema exactly, so the routing code never receives a malformed handoff. The schema is also where "context preserved" becomes enforceable: summary and attempted force the model to write the case notes — who the customer is, what they need, what was already tried — instead of dumping a raw transcript on the human. Your handler files the ticket, returns a tool_result (with "is_error": true if the handoff system itself failed, so the model can tell the user honestly), and Claude closes the conversation with a proper warm-transfer message.
Escalation in non-conversational pipelines
Document pipelines escalate too — an extraction job that hits an ambiguous clause should flag it, not guess. Here the tool pattern is overkill: add an escalation field to your structured output instead. A needs_review boolean plus a review_reason string in the output_config JSON schema lets every record self-sort into the straight-through pile or the human queue, at zero extra calls. Route anything with stop_reason: "refusal" to the same queue — a refusal is the model escalating on its own initiative, and per the structured-outputs docs it arrives as HTTP 200, so code must check for it explicitly.
Platform note
Everything above uses standard client-side tool use and structured outputs, both available across all four third-party platforms (structured outputs is a subset implementation on Bedrock and beta on Microsoft Foundry) — so escalation designs port across clouds unchanged.
Where to go next
See human-in-the-loop design for the workflow around the handoff, refusal handling for the model-initiated case, and customer support automation for a full worked example.