Skip to main content

Documentation Index

Fetch the complete documentation index at: https://orbit-docs.devotel.io/llms.txt

Use this file to discover all available pages before exploring further.

Agent handoff targets

A multi-agent setup typically has one routing / triage agent and several specialised agents (billing, support, sales, returns). When the routing agent decides “this is a billing question, hand off”, you want the destination to be a small explicit set — not “any agent in this org”. handoff_targets is that allowlist.

What it gates

Setting handoff_targets on agent A declares: “Agent A may hand off ONLY to the agents listed here.” The allowlist is enforced in two places:
  1. Runtime — the transfer_to_agent tool in the agent runtime refuses any target not on the source agent’s allowlist. The LLM never receives the option in its tool list.
  2. Internal handoff API (POST /internal/handoffs) — even with a valid internal token, a handoff is rejected with 403 FORBIDDEN if the target is not on the source agent’s allowlist. This protects the audit ledger and the agent.handoff.occurred webhook from forged entries.
When the allowlist is empty or absent, the agent has no peers it can hand off to. Set it explicitly to enable inter-agent routing.

Configure on agent create

curl -X POST https://orbit-api.devotel.io/api/v1/agents \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Triage Router",
    "description": "Classifies inbound messages and routes to a specialist.",
    "model": "gpt-4o",
    "instructions": "You are a customer service router for Acme Corp. Classify the inbound message and use transfer_to_agent to hand off to the right specialist.",
    "handoff_targets": [
      "agent_billing_specialist",
      "agent_support_specialist",
      "agent_returns_specialist"
    ]
  }'

Configure on agent update

curl -X PATCH https://orbit-api.devotel.io/api/v1/agents/agent_router \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "handoff_targets": [
      "agent_billing_specialist",
      "agent_support_specialist"
    ]
  }'
handoff_targets is persisted under the agent’s config.handoff_targets so the runtime can read it on every executor rebuild.

Update semantics

BodyBehaviour
"handoff_targets": ["agent_a", "agent_b"]Replaces the current list with the supplied array.
"handoff_targets": []Explicitly clears the list. The agent can no longer hand off.
Field omitted from PATCHLeaves the existing list untouched.

What a runtime handoff looks like

The router agent’s LLM sees transfer_to_agent in its tool list ONLY when handoff_targets is non-empty. The tool’s target_agent_id parameter is enumerated against the allowlist — the LLM cannot fabricate a target that isn’t on the list. A successful handoff:
  1. Persists an agent_handoffs row.
  2. Fans out a webhook event agent.handoff.occurred to the tenant’s subscription endpoint with source_agent_id, target_agent_id, conversation_id, reason, and summary.
  3. Re-enters the runtime with the target agent’s executor, carrying the conversation history forward.

What a refused handoff looks like

If a caller (most commonly an internal automation invoking POST /internal/handoffs directly) tries to record a handoff to an agent that isn’t on the source agent’s list:
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": {
    "code": "FORBIDDEN",
    "message": "The target agent is not on the source agent's handoff_targets allowlist.",
    "status": 403
  }
}
Refusals also emit a structured log line under agents.handoff_allowlist_violation with the source / target / allowlist size, so you can monitor for abuse.

Patterns

One-way fan-out (router → specialists)

agent_router.handoff_targets = [agent_billing, agent_support, agent_returns]
agent_billing.handoff_targets   = []   # specialists do not re-route
agent_support.handoff_targets   = []
agent_returns.handoff_targets   = []
This is the simplest topology and the recommended starting point.

Two-way (specialists can return to router)

agent_router.handoff_targets    = [agent_billing, agent_support]
agent_billing.handoff_targets   = [agent_router]
agent_support.handoff_targets   = [agent_router]
Useful when a specialist hits a question outside its scope and wants to bounce back to the router.

Mesh

agent_billing.handoff_targets = [agent_support]
agent_support.handoff_targets = [agent_billing]
Two specialists can hand off to each other directly without going through the router.

Common pitfalls

  • Forgetting to set handoff_targets — the LLM never sees the transfer_to_agent tool. Symptom: the agent responds itself even when it should escalate.
  • Listing an agent that doesn’t exist — the runtime ignores unknown ids; effectively the same as omitting them.
  • Listing the agent’s own id — allowed but pointless. The runtime handles transfer_to_agent with the same agent as a no-op.
  • Cross-tenant ids — every id is tenant-scoped. You cannot hand off to an agent in a different organisation.

See also