Handoff Tool

Transfer the call to another assistant.

The handoff tool enables seamless call transfers between assistants in a multi-agent system. This guide covers all configuration patterns and use cases.

Table of Contents

Overview

The handoff tool allows assistants to transfer calls to other assistants. Key features:

  • Transfer to specific assistants by ID or name (in a Squad)
  • Support for multiple destination options
  • Dynamic destination determination via webhook
  • Context manipulation during handoff
  • Variable extraction from conversations for subsequent assistants to use

System Prompt Best Practices

When using the handoff tool, add this to your system prompt for optimal agent coordination: https://openai.github.io/openai-agents-python/ref/extensions/handoff_prompt/

1# System context
2
3You are part of a multi-agent system designed to make agent coordination and execution easy.
4Agents uses two primary abstraction: **Agents** and **Handoffs**. An agent encompasses
5instructions and tools and can hand off a conversation to another agent when appropriate.
6Handoffs are achieved by calling a handoff function, generally named `handoff_to_<agent_name>`.
7Handoffs between agents are handled seamlessly in the background; do not mention or draw
8attention to these handoffs in your conversation with the user.
9
10# Agent context
11
12{put your agent system prompt here}

Basic Configuration

1. Single Destination Handoff

Using Assistant ID

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantId": "03e11cfe-4528-4243-a43d-6aded66ab7ba",
9 "description": "customer wants to speak with technical support",
10 "contextEngineeringPlan": {
11 "type": "all"
12 }
13 }
14 ]
15 }
16 ]
17}

Using Assistant Name (for Squad Members)

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantName": "TechnicalSupportAgent",
9 "description": "customer needs technical assistance",
10 "contextEngineeringPlan": {
11 "type": "all"
12 }
13 }
14 ]
15 }
16 ]
17}

Multiple Destinations

Best for OpenAI models - creates separate tool definitions for each destination:

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantId": "sales-assistant-123",
9 "description": "customer wants to learn about pricing or make a purchase",
10 "contextEngineeringPlan": {
11 "type": "all"
12 }
13 }
14 ]
15 },
16 {
17 "type": "handoff",
18 "destinations": [
19 {
20 "type": "assistant",
21 "assistantId": "support-assistant-456",
22 "description": "customer needs help with an existing product or service",
23 "contextEngineeringPlan": {
24 "type": "all"
25 }
26 }
27 ]
28 },
29 {
30 "type": "handoff",
31 "destinations": [
32 {
33 "type": "assistant",
34 "assistantId": "billing-assistant-789",
35 "description": "customer has questions about invoices, payments, or refunds",
36 "contextEngineeringPlan": {
37 "type": "lastNMessages",
38 "maxMessages": 5 // Only keeps the last 5 messages
39 }
40 }
41 ]
42 }
43 ]
44}

Best for Anthropic models - single tool with multiple destination options:

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantId": "03e11cfe-4528-4243-a43d-6aded66ab7ba",
9 "description": "customer wants to learn about pricing or make a purchase"
10 },
11 {
12 "type": "assistant",
13 "assistantName": "support-assistant",
14 "description": "customer needs help with an existing product or service"
15 },
16 {
17 "type": "assistant",
18 "assistantName": "billing-assistant",
19 "description": "customer has questions about invoices, payments, or refunds"
20 }
21 ]
22 }
23 ]
24}

Dynamic Handoffs

3.1 Basic Dynamic Handoff

The destination is determined at runtime via handoff-destination-request webhook:

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "dynamic",
8 "server": {
9 "url": "https://api.example.com/determine-handoff-destination",
10 "headers": {
11 "Authorization": "Bearer YOUR_API_KEY"
12 }
13 }
14 }
15 ]
16 }
17 ]
18}

Your server must respond to this request with a single destination. You may pass assistantId, assistantName (if using squads), or a transient assistant. For example:

1destination: {
2 "type": "assistant",
3 "assistantId": "assistant-id",
4 "variableExtractionPlan": {
5 "schema": {
6 "type": "object",
7 "properties": {
8 "name": {
9 "type": "string",
10 "description": "Name of the customer",
11 },
12 },
13 "required": ["name"],
14 },
15 },
16 "contextEngineeringPlan": {
17 "type": "none",
18 },
19},

If the handoff should not executed, either respond with an empty destination, or provide a custom error. The custom error will be added to the message history.

1{
2 error: "Example custom error message"
3}

3.2 Dynamic Handoff with Custom Parameters

Pass additional context to your webhook for intelligent routing:

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "dynamic",
8 "server": {
9 "url": "https://api.example.com/intelligent-routing"
10 }
11 }
12 ],
13 "function": {
14 "name": "handoff_with_context",
15 "description": "Transfer the call to the most appropriate specialist",
16 "parameters": {
17 "type": "object",
18 "properties": {
19 "destination": {
20 "type": "string",
21 "description": "Use 'dynamic' to route to the best available agent",
22 "enum": ["dynamic"]
23 },
24 "customerAreaCode": {
25 "type": "number",
26 "description": "Customer's area code for regional routing"
27 },
28 "customerIntent": {
29 "type": "string",
30 "enum": ["new-customer", "existing-customer", "partner"],
31 "description": "Customer type for proper routing"
32 },
33 "customerSentiment": {
34 "type": "string",
35 "enum": ["positive", "negative", "neutral", "escalated"],
36 "description": "Current emotional state of the customer"
37 },
38 "issueCategory": {
39 "type": "string",
40 "enum": ["technical", "billing", "sales", "general"],
41 "description": "Primary category of the customer's issue"
42 },
43 "priority": {
44 "type": "string",
45 "enum": ["low", "medium", "high", "urgent"],
46 "description": "Urgency level of the request"
47 }
48 },
49 "required": ["destination", "customerIntent", "issueCategory"]
50 }
51 }
52 }
53 ]
54}

Context Engineering

Control what conversation history is passed to the next assistant:

All Messages (Default)

1{
2 "contextEngineeringPlan": {
3 "type": "all"
4 }
5}

Last N Messages

1{
2 "contextEngineeringPlan": {
3 "type": "lastNMessages",
4 "maxMessages": 10
5 }
6}

No Context

1{
2 "contextEngineeringPlan": {
3 "type": "none"
4 }
5}

Variable Extraction

Extract and pass structured data during handoff. Variables extracted by the handoff tool are available to all subsequent assistants in the conversation chain. When a handoff extracts a variable with the same name as an existing one, the new value replaces the previous value.

1. variableExtractionPlan in destinations

This extraction method will make an OpenAI structured output request to extract variables. Use this method if you have multiple destinations, each with different variables that need to be extracted.

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantName": "order-processing-assistant",
9 "description": "customer is ready to place an order",
10 "variableExtractionPlan": {
11 "schema": {
12 "type": "object",
13 "properties": {
14 "customerName": {
15 "type": "string",
16 "description": "Full name of the customer"
17 },
18 "email": {
19 "type": "string",
20 "format": "email",
21 "description": "Customer's email address"
22 },
23 "productIds": {
24 "type": "array",
25 "items": {
26 "type": "string"
27 },
28 "description": "List of product IDs customer wants to order"
29 },
30 "shippingAddress": {
31 "type": "object",
32 "properties": {
33 "street": { "type": "string" },
34 "city": { "type": "string" },
35 "state": { "type": "string" },
36 "zipCode": { "type": "string" }
37 }
38 }
39 },
40 "required": ["customerName", "productIds"]
41 }
42 }
43 }
44 ]
45 }
46 ]
47}

2. tool.function

We will also extract variables in the tool call parameters from the LLM tool call (in addition to sending these parameters to your server in a handoff-destination-request in a dynamic handoff). Be sure to include the destination parameter with the assistant names or IDs in enum, as that is how Vapi determines where to handoff the call to. The destination parameter will not be extracted as a variable. Also, remember to add destination and all other variables that are required to the JsonSchema’s required array.

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "destinations": [
6 {
7 "type": "assistant",
8 "assistantName": "order-processing-assistant",
9 "description": "customer is ready to place an order",
10 }
11 ],
12 "function": {
13 "name": "handoff_to_order_processing_assistant",
14 "parameters": {
15 "type": "object",
16 "properties": {
17 "description": {
18 "type": "string",
19 "description": "The destination to handoff the call to.",
20 "enum": ["order-processing-assistant"]
21 },
22 "customerName": {
23 "type": "string",
24 "description": "Full name of the customer"
25 },
26 "email": {
27 "type": "string",
28 "format": "email",
29 "description": "Customer's email address"
30 },
31 "productIds": {
32 "type": "array",
33 "items": {
34 "type": "string"
35 },
36 "description": "List of product IDs customer wants to order"
37 },
38 "shippingAddress": {
39 "type": "object",
40 "properties": {
41 "street": { "type": "string" },
42 "city": { "type": "string" },
43 "state": { "type": "string" },
44 "zipCode": { "type": "string" }
45 }
46 }
47 },
48 "required": ["destination", "customerName", "email"]
49 }
50 }
51 },
52 ]
53}

Custom Function Definitions

Override the default function definition for more control. You can overwrite the function name for each tool to put into the system prompt or pass custom parameters in a dynamic handoff request.

1{
2 "tools": [
3 {
4 "type": "handoff",
5 "function": {
6 "name": "handoff_to_department",
7 "description": "Transfer the customer to the appropriate department based on their needs. Only use when explicitly requested or when the current assistant cannot help.",
8 "parameters": {
9 "type": "object",
10 "properties": {
11 "destination": {
12 "type": "string",
13 "description": "Department to transfer to",
14 "enum": ["sales-team", "technical-support", "billing-department", "management"]
15 },
16 "reason": {
17 "type": "string",
18 "description": "Brief reason for the transfer"
19 },
20 "urgency": {
21 "type": "boolean",
22 "description": "Whether this is an urgent transfer"
23 }
24 },
25 "required": ["destination", "reason"]
26 }
27 },
28 "destinations": [
29 {
30 "type": "assistant",
31 "assistantId": "sales-team",
32 "description": "Sales inquiries and purchases"
33 },
34 {
35 "type": "assistant",
36 "assistantId": "technical-support",
37 "description": "Technical issues and support"
38 },
39 {
40 "type": "assistant",
41 "assistantId": "billing-department",
42 "description": "Billing and payment issues"
43 },
44 {
45 "type": "assistant",
46 "assistantId": "management",
47 "description": "Escalations and complaints"
48 }
49 ]
50 }
51 ]
52}

Best Practices

  1. Clear Descriptions: Write specific, actionable descriptions for each destination in your sytem prompt. Use tool.function.name to customize the name of the function to put into your prompt.
  2. Context Management: Use lastNMessages to limit context size for performance
  3. Model Optimization: Use multiple tools for OpenAI, single tool for Anthropic
  4. Variable Extraction: Extract key data before handoff to maintain context
  5. Testing: Test handoff scenarios thoroughly, including edge cases

Troubleshooting

  • Ensure assistant IDs are valid and accessible
  • Verify webhook server URLs are reachable and return proper format
  • Check that required parameters in custom functions match destinations
  • Monitor context size to avoid token limits
  • Test variable extraction schemas with sample data
  • Validate that assistant names exist in the same squad

Last updated: August 2025 VAPI Documentation - Handoff Tool