Human-in-the-Loop (HITL)
Human-in-the-loop lets you pause agent communication and require manual approval before a message is forwarded. This is critical for high-stakes operations where you need a human to verify what an agent is about to do.How HITL Gets Triggered
There are two distinct ways a message can be held for human review:These are fundamentally different flows. Policy escalation is triggered by a detection policy (regex, Presidio, Comprehend) at the relay level — before or after the message reaches the agent, depending on which leg matched. Agent-initiated
INPUT_REQUIRED happens when the agent has processed the message and decided it needs human input. Both are automatically intercepted by the relay’s built-in HITL middleware.Setting Up Policy-Based HITL
To escalate messages for human review, set theaction to HUMAN_REVIEW_REQUIRED on any detection policy (regex, Presidio, or Comprehend). There is no separate “HITL policy type” — human review is an action you configure on your existing detection policies.
- API
- UI
Reviewing Approvals
When a message is held for review, it appears as a pending approval.List Pending Approvals
- API
- UI
Get a Specific Approval
- API
- UI
Key Fields in an Approval
Resolving Approvals
Approve
Approving a request allows the message to continue through the relay to the target agent.- API
- UI
- The held message is forwarded to the target agent
- The task resumes from where it was paused — callers polling
tasks/getwill seecompletedstate with the full agent response - The
relay_reasonmetadata is no longer present in the resolved response - The approval, resolution, and reviewer are recorded in the audit log
Reject
Rejecting a request stops the message from being forwarded. The task is cancelled.- API
- UI
- The message is not forwarded
- The associated task is cancelled — callers polling
tasks/getwill seecanceledstate withrelay_reason: "HITL_REJECTED"in the metadata (includingpolicy_name,policy_version, andpolicy_levelif the hold was policy-triggered) - The rejection reason is recorded in the audit log
Policy Escalation vs Agent INPUT_REQUIRED
It’s important to understand the difference between these two flows:Response Metadata — relay_reason
When the relay masks a response due to HITL (or a timeout), it sets a relay_reason field in the response metadata. This tells API consumers why the relay has taken ownership of the task. The response metadata is a strongly-typed discriminated union — the shape of the metadata depends on the relay_reason value.
Policy fields (
policy_name, policy_version, policy_level) are included for HITL_HELD and HITL_REJECTED to give consumers context about which policy triggered the hold. These fields are nullable on HITL_REJECTED — they will be null if the original hold was agent-initiated rather than policy-triggered.
policy_level indicates the scope at which the policy is bound: TENANT, AGENT, or SUBSCRIPTION.
Example metadata for a policy-held response:
For a TypeScript integration, use the
@swarmd/channel-client lifecycle events to react to working, hitl-held, completed, rejected, failed, and canceled without implementing polling yourself. The raw Conversation API frontend tutorial documents the underlying aggregateState / relay_reason protocol, and the older JSON-RPC flow is documented separately at Frontend Integration (JSON-RPC — legacy).Auditing HITL Decisions
Every HITL event is recorded in the audit log with specific audit types:
Query HITL-related events:
- API
- UI
correlationId from the approval:Required Permissions
To manage HITL approvals, users need the following permissions (granted through group membership):
The default Editor group includes both of these permissions. See the User Management tutorial for how to manage groups and permissions.
Next Steps
TypeScript Channel Client
Handle HITL transitions through typed channel lifecycle events.
Monitoring & Audit
Track all agent activity and HITL decisions in the audit log.
