When an agent chain includes a HITL step (human review required), the relay holds the blocked agent’s response until an admin approves or rejects it. Your frontend needs to detect this and poll for the final result.The relay is designed so that the entire chain resolves before the caller gets a completed answer. You never need to stitch together partial responses from multiple agents. You send one request, poll if needed, and get one complete answer.
What to do: Render result.status.message as the assistant’s response. Done.How to detect:result.status.state === "completed" and no result.metadata.hitl_pending and no result.metadata.relay_task.
A downstream agent in the chain requires human review. The relay returned early because the chain is still processing in the background. This is the most common HITL scenario when agents use the Swarmd SDK.
Poll tasks/get using that ID until the state changes
How to detect:result.status.state === "working" and result.metadata.relay_task === true.Polling request (same endpoint, same agent ID as the original request):
After admin approves — poll returns completed with the full answer:
{ "jsonrpc": "2.0", "id": "poll-002", "result": { "id": "e6a262db-...", "contextId": "17aa30cf-...", "status": { "state": "completed", "message": { "role": "agent", "parts": [{ "kind": "text", "text": "Horizon Visa Platinum has been assigned to Emma Alvarez (CU-1340). The product is now active in her portfolio." }] } }, "kind": "task" }}
This response is the complete, combined answer from the entire agent chain. The parent agent waited for the sub-agent’s HITL to resolve, got the sub-agent’s result, and formulated a single response. Render it as a normal assistant message.If admin rejects — poll returns failed:
3. Completed with hitl_pending — fallback for legacy agents
This scenario only occurs when agents are not using PollingRemoteA2aAgent from the Swarmd SDK. Once all agents are updated to use the SDK, this scenario does not happen — you will always get scenario 1 or 2.
The parent agent returned its own response (completed), but a downstream agent is still blocked by HITL. The parent did not wait.
{ "jsonrpc": "2.0", "id": "msg-001", "result": { "id": "e6a262db-...", "contextId": "17aa30cf-...", "status": { "state": "completed", "message": { "role": "agent", "parts": [{"kind": "text", "text": "I've submitted the product assignment. It requires approval before it can be processed."}] } }, "kind": "task", "metadata": { "hitl_pending": { "hitl_request_id": "486ebe5c-...", "task_id": "96047697-...", "context_id": "7b290f0f-...", "sink_agent_id": "d8e9bef0-...", "detection_source": "AGENT_INPUT_REQUIRED" } } }}
What to do:
Render the parent agent’s partial response
Show a “Pending review” indicator
Poll tasks/get on the blocked sub-agent using hitl_pending.sink_agent_id and hitl_pending.task_id
When resolved, render the sub-agent’s result alongside the parent’s response
How to detect:result.status.state === "completed" and result.metadata.hitl_pending is present.Polling request (note: different agent ID — the blocked sub-agent):
Receive message/send response | vIs state "working" + relay_task? YES → Show "Pending review", poll tasks/get with result.id NO ↓Is state "completed" + hitl_pending? YES → Render partial answer, poll sub-agent for remaining result NO ↓Is state "completed" (no hitl_pending)? YES → Render full answer. Done.
When agents use PollingRemoteA2aAgent from the Swarmd Python SDK, the full chain blocks until every sub-agent completes:
Human sends message/send to Agent A ↓Relay starts processing (30s timeout) ↓Agent A calls Agent B via relay ↓Agent B is HITL-blocked → relay masks as "working" ↓Agent A's PollingRemoteA2aAgent polls Agent B every 5s ↓Relay returns "working" to human (30s timeout exceeded) ↓Human polls tasks/get → "working" ↓Admin approves HITL → Agent B completes ↓Agent A receives Agent B's result → formulates combined answer ↓Relay stores completed result in task store ↓Human polls tasks/get → "completed" with full answer
The human gets one complete response that includes data from all agents in the chain. No partial answers, no manual stitching.