Logo
Search
API Docs

Squad Routing

Squads

Squad Routing: Handoff Destinations & Context Engineering

Overview

Squads & Handoffs and the Squad API page introduce squads and summarize the handoff mechanism. This page goes deeper into how routing actually gets decided: the assistantDestinations shorthand, the three destination types, why the description field is the single most important part of a handoff destination, the two multi-destination patterns for different LLM providers, the full set of contextEngineeringPlan options, and dynamic runtime routing.


The Handoff Tool & assistantDestinations

The handoff tool (type: "handoff") is the primary mechanism for transferring a call between assistants. You can configure it directly as a tool with a destinations array, or use assistantDestinations on a squad member as a shorthand that configures the handoff tool for you behind the scenes — useful when you want to define routing directly alongside a squad member's definition rather than as a separate tool object.

"assistantDestinations": [
    {
        "type": "assistant",
        "assistantName": "Billing Specialist",
        "description": "Customer has a question about a charge, invoice, or refund."
    }
]

Three Destination Types

TypeBehavior
assistantTransfers to a single named or ID-referenced assistant, either standalone or as a squad member
squadTransfers into an entire nested squad rather than a single assistant, letting that squad's own internal routing take over from there
dynamicCalls a webhook server at runtime to determine the destination, rather than routing to a statically configured target

The description Field: Writing the Trigger Condition

Every static destination (assistant or squad) needs a description field, and it matters more than any other field on the destination. The model uses this text to decide when to trigger that specific transfer — it is effectively the condition, written in plain language, that tells the model "transfer here when this is true."

Write it from the customer's perspective, as a specific triggering condition, not a generic label:

WeakBetter
"Billing""Customer has a question about a charge, invoice, refund, or their subscription plan."
"Technical support""Customer is reporting that a product or service isn't working as expected and needs troubleshooting help."

When a squad member has multiple destinations, each one's description needs to be distinct enough that the model can reliably tell which condition applies — overlapping or vague descriptions are the most common cause of a squad routing to the wrong specialist.


Multi-Destination Patterns by LLM Provider

When an assistant needs to route to more than one possible destination, the recommended pattern depends on which LLM provider the assistant uses:

PatternRecommended forDescription
Multiple handoff tools, one destination eachOpenAI modelsDefine a separate handoff tool per destination, each with its own single destinations entry and description
Single handoff tool, multiple destinationsAnthropic modelsDefine one handoff tool with a destinations array listing every possible target

This is a provider-behavior recommendation, not a hard restriction — either pattern will technically function with either provider, but following the recommended pattern for your chosen model tends to produce more reliable routing decisions.


contextEngineeringPlan: Controlling What Transfers

contextEngineeringPlan is set on a handoff destination (or an assistantDestinations entry) and controls exactly what conversation history is forwarded to the next assistant when the transfer happens.

TypeBehavior
allTransfers the entire conversation history, including tool call results (the default)
lastNMessagesTransfers only the most recent N messages
userAndAssistantMessagesTransfers only user and assistant messages, filtering out system messages, tool calls, and tool results
previousAssistantMessagesTransfers only conversation history from before the current assistant's session — commonly used when handing off from a sensitive assistant such as one handling payment data
noneStarts the next assistant with a blank conversation, for maximum isolation
{
    "type": "assistant",
    "assistantName": "Confirmation Assistant",
    "description": "Transfer here once payment collection is complete.",
    "contextEngineeringPlan": {
        "type": "previousAssistantMessages"
    }
}

If you don't set a contextEngineeringPlan, the default all forwards everything, including tool call results — worth double-checking on any handoff coming from an assistant that handled sensitive data during its turn.


Dynamic Routing via Webhook

When the correct destination can't be known ahead of time and needs to be resolved at runtime — for example, based on a database lookup or business logic outside the assistant's own reasoning — use a dynamic destination:

{
  "tools": [
    {
      "type": "handoff",
      "destinations": [
        {
          "type": "dynamic",
          "server": {
            "url": "https://api.example.com/determine-handoff-destination"
          }
        }
      ]
    }
  ]
}

Your server receives the handoff request and responds with the assistant to route to, or an error to cancel the handoff outright and keep the call with the current assistant.


Worked Example: A Property-Management Router Squad

A common pattern is a single "Router" assistant that greets every caller, classifies the nature of the inquiry, and routes to a specialist:

{
  "members": [
    {
      "assistant": {
        "name": "Router",
        "firstMessage": "Thanks for calling. Are you calling about a maintenance issue, a lease question, or something else?",
        "model": {
          "provider": "openai",
          "model": "gpt-4o",
          "messages": [{ "role": "system", "content": "Classify the caller's inquiry and transfer to the correct specialist." }]
        }
      },
      "assistantDestinations": [
        {
          "type": "assistant",
          "assistantName": "Maintenance Specialist",
          "description": "Caller is reporting a maintenance issue, repair need, or something broken in their unit.",
          "contextEngineeringPlan": { "type": "all" }
        },
        {
          "type": "assistant",
          "assistantName": "Leasing Specialist",
          "description": "Caller has a question about renting a unit, lease terms, renewals, or move-in/move-out.",
          "contextEngineeringPlan": { "type": "all" }
        }
      ]
    },
    {
      "assistant": {
        "name": "Maintenance Specialist",
        "model": { "provider": "openai", "model": "gpt-4o", "messages": [{ "role": "system", "content": "Handle maintenance requests: gather unit number, issue description, and urgency." }] }
      }
    },
    {
      "assistant": {
        "name": "Leasing Specialist",
        "model": { "provider": "openai", "model": "gpt-4o", "messages": [{ "role": "system", "content": "Handle leasing questions: availability, pricing, and application steps." }] }
      }
    }
  ]
}

Notice each destination's description is a specific triggering condition, not a generic label — this is what makes the router reliable at picking between the two specialists.

In summary: assistantDestinations is a convenient shorthand over the handoff tool, the description field is the actual routing logic the model reasons over, multi-destination structure should match your LLM provider's recommended pattern, contextEngineeringPlan controls exactly what history follows the caller to the next assistant, and dynamic destinations hand routing decisions off to your own server when static rules aren't enough.