A research agent finds a customer account, confirms the plan, and retrieves the last four invoices. It hands billing a one-line summary—“Customer wants a refund”—and the billing agent immediately asks for the account ID. The facts gathered by the first agent disappeared at the handoff boundary.
That failure costs twice: duplicated API calls and decisions made with incomplete information. Lost state is a core production-agent failure mode; in multi-agent systems, the handoff is where it becomes visible.
The practical fix is to define what must survive, validate it at the boundary, and pass references instead of bulky prompt payloads whenever possible. Apidog helps because shared API definitions and mocks let every agent retrieve the same records consistently.
What must survive a handoff
Do not copy the entire conversation. The receiving agent gets a full context window and still has to determine which details matter.
Instead, carry four categories:
Identifiers
Account IDs, order IDs, job IDs, ticket numbers, and subscription IDs. These are small, stable, and let the next agent retrieve authoritative data. They are also the most commonly omitted fields.Decisions already made
For example: “The customer is eligible for a refund under policy 3.” The next agent should not re-litigate an approved decision.Constraints
Budget limits, approvals, and completed actions. Losing these can cause duplicate charges, repeated emails, or repeated approval requests. Pair this with idempotency keys for AI agents.Open questions
Explicitly record unresolved items so the receiving agent asks instead of assuming.
Do not hand off raw API responses, chain-of-thought-style reasoning transcripts, or data the receiving agent can fetch in one call.
Choose a state-passing strategy
1. Pass the full conversation
This can work for two agents in a short task. It breaks down as histories grow: relevant facts get buried, and the receiving agent spends its token budget reading old tool output. See why tool responses should stay out of the context window.
2. Pass a prose summary
This is common and easy to implement, but it is predictably lossy. Models summarize toward narrative:
“The customer has subscribed for two years and is frustrated.”
What the billing agent actually needs is:
customer_id: cus_8812,plan: pro, four invoice IDs, and the refund decision.
3. Pass a structured handoff object
This takes more setup, but it scales. The sender fills a schema; the receiver reads fields instead of interpreting prose.
{
"task_id": "task_2026_08_26_0031",
"from_agent": "research",
"to_agent": "billing",
"entities": {
"customer_id": "cus_8812",
"invoice_ids": ["inv_41", "inv_42", "inv_43", "inv_44"],
"subscription_id": "sub_119"
},
"decisions": [
{
"decision": "refund_eligible",
"value": true,
"basis": "policy 3.2, charged twice in one cycle"
}
],
"constraints": {
"max_refund_cents": 4900,
"human_approval_granted": false,
"actions_taken": ["read_invoices"]
},
"open_questions": [
"Customer has not confirmed which invoice to refund"
],
"summary": "Customer cus_8812 was double-charged in August. Refund of one invoice is approved under policy 3.2, up to 4900 cents. Awaiting the customer's choice of invoice."
}
Keep the summary field for nuance, but never use it as a replacement for fields that must be validated.
Validate before transfer:
- Fail if required IDs are missing.
- Reject malformed decisions or constraints.
- Require an explicit list of completed actions.
- Require an explicit list of open questions, even if empty.
Failing at the boundary is much safer than discovering missing state three API calls later.
Pass references, not payloads
The strongest handoff often contains almost no data: it passes IDs, and the receiving agent fetches current data.
This has three benefits:
- Fresh state: the next agent sees updates made after the first agent ran.
- Small prompts: a few hundred bytes of identifiers replace thousands of tokens.
- Better auditability: reads appear as API calls instead of copied prompt text.
For example, pass customer_id and invoice_ids, then let the billing agent call the billing API directly.
This requires correctly scoped access. Every agent should have separate credentials limited to its role. A billing agent needs refund permissions; a research agent does not. See least-privilege API keys for agents.
If a refetch is expensive, cache the result in the orchestrator and pass a cache reference. The receiver should still request the data explicitly.
Prevent the failures that cause most incidents
Dropped identifiers
A summary says “the customer,” but includes no account ID. The next agent searches by name, finds multiple matches, and selects the wrong account.
Prevent it: make entity IDs required schema fields and validate them before handoff.
Repeated actions
The first agent sends an email, but does not record it. The second agent sends it again.
Prevent it: record actions_taken, check it before writes, and use idempotency keys so repeats are harmless.
Lost approvals
A human approves a refund while the first agent runs. The second agent does not know and asks again.
Prevent it: carry approvals as task-scoped constraints, not agent-scoped memory.
Confident invention
The receiving agent needs a value that is absent from the handoff and invents a plausible one instead of asking.
Prevent it: use open_questions and enforce a receiver rule:
If a required identifier is absent, stop and ask for it.
Do not infer or fabricate IDs, amounts, approvals, or completed actions.
State decay in loops
When agent A hands off to B and B hands back to A, repeated summarization degrades state.
Prevent it:
- Carry one original task object through every hop.
- Update it in place.
- Cap the number of hops.
- Revisit decomposition if a task needs more than a handful of transfers.
Test the boundary, not only the agents
Handoffs are integration points. Test them accordingly.
Assert on the sender’s handoff object
Run the sender against a fixed scenario and verify:
- Required identifiers are present.
- Decisions include their basis.
- Completed actions are listed.
- Constraints and approvals are preserved.
- Open questions are explicit.
This is a deterministic assertion on a structured payload, even if the agent itself is non-deterministic. See testing non-deterministic agents.
Test the receiver in isolation
Feed the receiver a valid handoff object and verify its behavior. Then remove customer_id and confirm that it asks for clarification rather than guessing.
The deliberately incomplete-handoff test is what catches confident invention.
Run both agents against mocks
A test that issues a real refund is not a test suite you will run often. Point agents to mocked APIs so tests can run on every change.
With Apidog mocks, both agents can use the same API definition, reducing contract drift. See how to run agents against mocks instead of production.
Log every handoff
Store the complete handoff object with the task ID at every boundary. When a workflow fails, the log shows which agent had the missing fact and where it disappeared.
Also record tool activity and API calls; tracing agent tool calls covers the rest of that audit trail.
Know what your framework actually transfers
Most orchestration frameworks provide a handoff primitive, but they do not decide which facts are load-bearing.
The OpenAI Agents SDK handoff model treats handoff as a tool call. That is convenient, but it means the model decides when control transfers. Validate output before the receiver starts.
LangGraph multi-agent patterns use an explicit graph state object shared across nodes. This maps naturally to a structured handoff schema; your remaining job is to define required fields.
Anthropic’s article on building a multi-agent research system is useful for understanding how much instruction a sub-agent needs to work independently.
Frameworks move something. Your schema determines whether they move what matters.
Keep task state outside the conversation
Store durable task state by task_id, and have every agent read and update it.
Conversation history is a poor state container because it can be truncated, compacted, or rewritten by summarization. A durable record does not lose a required field because a model decided it was less narratively important.
A simple execution pattern looks like this:
- Agent loads the task object at the start of its turn.
- Agent retrieves records by ID.
- Agent appends completed work to
actions_taken. - Agent saves decisions, constraints, and open questions.
- On handoff, agent passes only
task_idand essential references. - Receiving agent loads the same task object.
If a run fails midway, the next run resumes from durable state instead of reconstructing the task from prompt history.
When a platform already models the task
If agents run as CLI runtimes on developer machines, you may need to build the durable task object yourself. Work-management platforms can provide it.
Sharkly models work around a Task that holds the goal, status, responsible person, assigned Agent or Crew, comments, execution state, and result. A Crew groups a leader Agent with other agents and people, allowing reusable specialist teams rather than repeated prompt-to-prompt transfers.
The runtimes can remain the tools you already use—Claude Code, Codex, and others—while the platform manages task records, assignment, and review. Sharkly’s documentation is a useful reference for the fields durable multi-agent tasks tend to need.
Handoff checklist
- [ ] Define and validate a handoff schema.
- [ ] Make entity identifiers required fields.
- [ ] Record decisions with their basis.
- [ ] Record completed actions and check them before writes.
- [ ] Carry approvals and budgets with the task.
- [ ] Make unresolved questions explicit.
- [ ] Pass data by reference when refetching is cheap.
- [ ] Cap hop count and preserve the original task object.
- [ ] Log every handoff with its task ID.
- [ ] Run CI boundary tests against mocks, including incomplete-handoff cases.
Frequently asked questions
Is a structured handoff worth it for only two agents?
For a short, two-agent task, passing conversation history can be sufficient. Use a structured object when tasks are long, involve three or more agents, or cross process or run boundaries.
Should the model create the handoff object?
Use code wherever possible. Your orchestrator should populate identifiers, completed actions, and approvals from actual system events. Let the model contribute only narrative summary and open questions.
How do I stop context decay in a loop?
Maintain one task object for the entire run and update it in place. Do not regenerate state at each transfer. Also cap hops; excessive handoffs usually indicate poor task decomposition.
What if my framework already supports handoffs?
Use its handoff primitive, but inspect what it transfers. Many frameworks pass only message history, which means IDs survive only if they happen to appear in prose. Add a validated structured payload alongside framework state.
Do sub-agents need separate API credentials?
Yes. Scope credentials to each agent’s responsibilities. Sharing one powerful key increases blast radius and makes attribution harder.
How much belongs in the summary field?
Keep it to a few sentences of intent and nuance. IDs, amounts, approvals, and actions belong in structured fields where they can be validated.
Most multi-agent failures are not reasoning failures. They are facts that existed in one agent but not in the next. Treat the handoff as an interface: define a schema, validate it, test it, and keep durable state outside the prompt.
Download Apidog to keep API definitions, mocks, and boundary tests close to the APIs your agents share.
Top comments (0)