Transfer control between specialized agents during execution.
Create handoffs to delegate work to other agents.
/**
* Create a handoff to delegate to another agent
* @param agent - Target agent to handoff to
* @param options - Handoff configuration (inline options object, not a named export)
* @returns Handoff instance
*/
function handoff<TContext, TOutput, TInputType>(
agent: Agent<TContext, TOutput>,
options?: {
/** Override handoff tool name */
toolNameOverride?: string;
/** Override handoff tool description */
toolDescriptionOverride?: string;
/** Callback invoked when handoff occurs */
onHandoff?: (
context: RunContext<TContext>,
input?: any
) => void | Promise<void>;
/** Input schema for handoff parameters */
inputType?: TInputType;
/** Filter/transform handoff input data */
inputFilter?: (input: HandoffInputData) => HandoffInputData;
/** Conditionally enable/disable handoff */
isEnabled?: boolean | ((options: {
runContext: RunContext<TContext>;
agent: Agent;
}) => boolean | Promise<boolean>);
}
): Handoff<TContext, TOutput>;
interface HandoffInputData {
/** Handoff message/context */
message?: string;
/** Additional handoff parameters */
[key: string]: any;
}Usage Examples:
import { Agent, run, handoff } from '@openai/agents';
// Basic handoff
const specialist = new Agent({
name: 'Specialist',
instructions: 'You are a domain expert',
});
const generalist = new Agent({
name: 'Generalist',
instructions: 'You handle general queries and delegate to specialists',
handoffs: [handoff(specialist)],
});
const result = await run(
generalist,
'I need expert help with a complex problem'
);
// Custom handoff description
const techSupport = new Agent({
name: 'TechSupport',
instructions: 'You provide technical support',
});
const mainAgent = new Agent({
name: 'MainAgent',
instructions: 'You are the main customer service agent',
handoffs: [
handoff(techSupport, {
toolDescriptionOverride: 'Transfer to technical support for hardware or software issues',
}),
],
});
// With handoff callback
const salesAgent = new Agent({
name: 'SalesAgent',
instructions: 'You handle sales inquiries',
});
const routerAgent = new Agent({
name: 'Router',
instructions: 'You route customers to the right department',
handoffs: [
handoff(salesAgent, {
onHandoff: async (context, input) => {
console.log(`Transferring to sales: ${input?.message}`);
// Log handoff, update metrics, etc.
},
}),
],
});
// Conditional handoff
const premiumAgent = new Agent({
name: 'PremiumSupport',
instructions: 'You provide premium support',
});
const tieredAgent = new Agent({
name: 'TieredSupport',
instructions: 'You provide tiered customer support',
handoffs: [
handoff(premiumAgent, {
isEnabled: ({ runContext }) => {
// Only enable for premium customers
return runContext.context.customerTier === 'premium';
},
}),
],
});
const result2 = await run(tieredAgent, 'I need help', {
context: { customerTier: 'premium' },
});
// With input filtering
const filteredAgent = new Agent({
name: 'FilteredAgent',
instructions: 'You handle filtered requests',
});
const filteringAgent = new Agent({
name: 'FilteringAgent',
instructions: 'You route with filtering',
handoffs: [
handoff(filteredAgent, {
inputFilter: (input) => {
// Remove sensitive data before handoff
const filtered = { ...input };
delete filtered.ssn;
delete filtered.creditCard;
return filtered;
},
}),
],
});The Handoff class represents a delegation point to another agent.
/**
* Handoff instance for delegating to another agent
*/
class Handoff<TContext = any, TOutput = any> {
/** Handoff tool name */
toolName: string;
/** Handoff tool description */
toolDescription: string;
/** JSON schema for handoff input */
inputJsonSchema: JsonObjectSchema;
/** Target agent */
agent: Agent<TContext, TOutput>;
/**
* Invoke the handoff
* @param context - Run context
* @param args - Handoff arguments
* @returns Target agent
*/
onInvokeHandoff(
context: RunContext<TContext>,
args?: any
): Agent<TContext, TOutput> | Promise<Agent<TContext, TOutput>>;
/**
* Get handoff as a function tool
* @returns FunctionTool representing the handoff
*/
getHandoffAsFunctionTool(): FunctionTool;
}Usage Examples:
import { Agent, Handoff, handoff } from '@openai/agents';
const targetAgent = new Agent({
name: 'Target',
instructions: 'You are the target agent',
});
const handoffInstance = handoff(targetAgent, {
toolNameOverride: 'transfer_to_target',
toolDescriptionOverride: 'Transfer control to the target agent',
});
// Access handoff properties
console.log(handoffInstance.toolName); // 'transfer_to_target'
console.log(handoffInstance.toolDescription); // 'Transfer control to the target agent'
console.log(handoffInstance.agent.name); // 'Target'
// Convert to function tool
const tool = handoffInstance.getHandoffAsFunctionTool();Utility functions for working with handoffs.
/**
* Get or create Handoff instance from agent or handoff
* @param agentOrHandoff - Agent or existing Handoff
* @returns Handoff instance
*/
function getHandoff(agentOrHandoff: Agent | Handoff): Handoff;
/**
* Generate transfer message for handoff
* @param agent - Agent being transferred to
* @returns Transfer message text
*/
function getTransferMessage(agent: Agent): string;Usage Examples:
import { Agent, handoff, getHandoff, getTransferMessage } from '@openai/agents';
const agent = new Agent({
name: 'HelperAgent',
instructions: 'You help with tasks',
});
// Get handoff from agent
const handoffInstance = getHandoff(agent);
console.log(handoffInstance.toolName); // 'transfer_to_HelperAgent'
// Get handoff from existing handoff
const customHandoff = handoff(agent, {
toolNameOverride: 'custom_transfer',
});
const sameHandoff = getHandoff(customHandoff);
console.log(sameHandoff === customHandoff); // true
// Generate transfer message
const message = getTransferMessage(agent);
console.log(message); // 'Transferring to HelperAgent...'Define handoffs directly in the agent configuration without explicit handoff() calls.
interface AgentConfig {
/** Sub-agents this agent can handoff to (agents or handoffs) */
handoffs?: (Agent | Handoff)[];
}Usage Examples:
import { Agent, run } from '@openai/agents';
// Define target agents
const techSupport = new Agent({
name: 'TechSupport',
handoffDescription: 'Technical support for hardware and software issues',
instructions: 'You provide technical support',
});
const billing = new Agent({
name: 'Billing',
handoffDescription: 'Billing and payment inquiries',
instructions: 'You handle billing questions',
});
const sales = new Agent({
name: 'Sales',
handoffDescription: 'Sales and product information',
instructions: 'You help with sales inquiries',
});
// Router agent with inline handoffs
const router = new Agent({
name: 'Router',
instructions: 'You route customers to the appropriate department',
handoffs: [techSupport, billing, sales], // Pass agents directly
});
const result = await run(
router,
'I have a question about my bill'
);
// Handoff descriptions are used automatically
// The model sees tools like:
// - transfer_to_TechSupport: "Technical support for hardware and software issues"
// - transfer_to_Billing: "Billing and payment inquiries"
// - transfer_to_Sales: "Sales and product information"Use Agent.create() for automatic output type inference when using handoffs.
/**
* Create agent with automatic output type inference from handoffs
* @param config - Agent configuration
* @returns Agent with inferred output type
*/
static Agent.create<TOutput, Handoffs>(
config: AgentConfig
): Agent<any, ResolvedAgentOutput<TOutput, Handoffs>>;
type ResolvedAgentOutput<TOutput, Handoffs> =
| string // Text output
| TOutput // Structured output
| Agent; // Handoff output (union of all handoff target agents)Usage Examples:
import { Agent, run } from '@openai/agents';
const specialist1 = new Agent({
name: 'Specialist1',
instructions: 'You are specialist 1',
});
const specialist2 = new Agent({
name: 'Specialist2',
instructions: 'You are specialist 2',
});
// Use Agent.create() for proper type inference
const router = Agent.create({
name: 'Router',
instructions: 'You route to specialists',
handoffs: [specialist1, specialist2],
});
const result = await run(router, 'I need specialist help');
// finalOutput is typed as: string | Agent
if (result.finalOutput instanceof Agent) {
console.log(`Handed off to: ${result.finalOutput.name}`);
} else {
console.log(`Response: ${result.finalOutput}`);
}Create hierarchical agent structures with multiple handoff levels.
Usage Examples:
import { Agent, run } from '@openai/agents';
// Level 3: Specialists
const hardwareExpert = new Agent({
name: 'HardwareExpert',
handoffDescription: 'Expert in hardware issues',
instructions: 'You are a hardware specialist',
});
const softwareExpert = new Agent({
name: 'SoftwareExpert',
handoffDescription: 'Expert in software issues',
instructions: 'You are a software specialist',
});
// Level 2: Department agents
const techSupport = new Agent({
name: 'TechSupport',
handoffDescription: 'Technical support department',
instructions: 'You provide technical support and can escalate to specialists',
handoffs: [hardwareExpert, softwareExpert],
});
const customerService = new Agent({
name: 'CustomerService',
handoffDescription: 'Customer service department',
instructions: 'You handle general customer inquiries',
});
// Level 1: Main router
const mainRouter = new Agent({
name: 'MainRouter',
instructions: 'You are the main customer service router',
handoffs: [techSupport, customerService],
});
// The agent can handoff through multiple levels
const result = await run(
mainRouter,
'My laptop screen is flickering'
);
// Might handoff: MainRouter -> TechSupport -> HardwareExpertDefine structured input schemas for handoffs.
Usage Examples:
import { z } from 'zod';
import { Agent, run, handoff } from '@openai/agents';
const orderProcessor = new Agent({
name: 'OrderProcessor',
instructions: 'You process customer orders',
});
const salesAgent = new Agent({
name: 'SalesAgent',
instructions: 'You handle sales inquiries and can process orders',
handoffs: [
handoff(orderProcessor, {
inputType: z.object({
orderId: z.string(),
customerId: z.string(),
priority: z.enum(['low', 'medium', 'high']),
notes: z.string().optional(),
}),
}),
],
});
const result = await run(
salesAgent,
'I need to process order #12345 for customer C-9876 with high priority'
);Filter and transform data before handoffs occur.
Usage Examples:
import { Agent, run, handoff, Runner } from '@openai/agents';
const secureAgent = new Agent({
name: 'SecureAgent',
instructions: 'You handle sensitive data',
});
// Agent-level input filter
const mainAgent = new Agent({
name: 'MainAgent',
instructions: 'You process customer requests',
handoffs: [
handoff(secureAgent, {
inputFilter: (input) => {
// Remove PII before handoff
return {
...input,
ssn: '[REDACTED]',
creditCard: '[REDACTED]',
};
},
}),
],
});
// Global handoff input filter
const runner = new Runner({
handoffInputFilter: (input) => {
// Apply global filtering to all handoffs
console.log('Handoff occurring:', input);
return input;
},
});
await runner.run(mainAgent, 'Process my information');Access handoff information in RunResult.
Usage Examples:
import { Agent, run } from '@openai/agents';
const specialist = new Agent({
name: 'Specialist',
instructions: 'You are a specialist',
});
const router = new Agent({
name: 'Router',
instructions: 'You route to specialists',
handoffs: [specialist],
});
const result = await run(router, 'I need specialist help');
// Check if handoff occurred
if (result.finalOutput instanceof Agent) {
console.log(`Handed off to: ${result.finalOutput.name}`);
}
// Check last agent
console.log(`Last active agent: ${result.lastAgent?.name}`);
// Inspect run items for handoff calls
for (const item of result.newItems) {
if (item instanceof RunHandoffCallItem) {
console.log('Handoff invoked:', item.handoffCall.name);
} else if (item instanceof RunHandoffOutputItem) {
console.log('Handoff completed:', item.handoffOutput);
}
}Continue execution after a handoff by running the returned agent.
Usage Examples:
import { Agent, run } from '@openai/agents';
const specialist = new Agent({
name: 'Specialist',
instructions: 'You are a specialist. After helping, return to the router.',
outputType: 'handoffs', // Indicate this agent can handoff back
});
const router = new Agent({
name: 'Router',
instructions: 'You route to specialists',
handoffs: [specialist],
});
// Add bidirectional handoffs
specialist.handoffs = [router];
// Initial run
const result1 = await run(router, 'I need specialist help');
if (result1.finalOutput instanceof Agent) {
console.log(`Handed off to: ${result1.finalOutput.name}`);
// Continue conversation with the specialist
const result2 = await run(
result1.finalOutput,
'Thank you, that helps!',
{ context: result1.state.runContext.context }
);
if (result2.finalOutput instanceof Agent) {
console.log(`Handed back to: ${result2.finalOutput.name}`);
}
}