Skip to main content
Human-in-the-Loop (HITL) suspends an agent run just before a tool call and waits for a person to approve, edit or decline it. The approval card shows the tool, the arguments the model proposed, and lets the reviewer change those arguments before the tool runs. HITL is a per-tool gate. It is not a global switch — each tool is either gated or not, and three independent layers decide which.
HITL requires checkpointing. Set persistent_state: true on the system config, otherwise the configuration fails to compile with:
The run has to be suspended and resumed later, which is impossible without a checkpointer.

Quick start

Gate one tool for one agent:
A tool entry can be either a bare string (no HITL config possible) or an object. Only the object form accepts hitl_config.

The three layers

Whether a given call pauses is decided by a precedence ladder. Higher layers win and cannot be turned off by lower ones.
1

Tool source (strongest)

The tool author declares hitl_requirement in the tool’s own code. Three values:
  • mandatory — always pauses. Nothing can switch it off. Used for irreversible operations (delete, deactivate, financial transactions). Example: mail_delete_email.
  • default_on — pauses by default, but can be switched off per instance with disable_human_approval. Used for consequential but reversible actions. Example: mail_send_email, most TwentyCRM and Jira write tools.
  • never (the default) — no source-level gate. Read-only and otherwise safe tools.
2

Policy mandate

mandate_human_approval: true forces a gate onto a tool that has no source-level declaration — typically an MCP server tool or another foreign tool whose code you do not own. Stronger than the per-instance opt-in below and cannot be ratcheted off by it.
3

Per-instance opt-in (weakest)

requires_human_approval: true gates this particular use of the tool. Purely additive — it can never disable a gate, only add one.

Ratchet rules

disable_human_approval is honoured only against a default_on source. Against mandatory or against any policy mandate it is ignored without error.

Configuration reference

hitl_config sits on a tool object inside an agent’s tools list.
object
Per-instance Human-in-the-Loop configuration for a single use of a tool.
hitl_config rejects unknown keys. A typo (for example require_human_approval without the s) makes the whole tool config invalid and the HITL knobs are dropped — check the validation output after editing.

Examples

Gate a tool that has no source-level requirement

calculator declares never. Opt in per instance:

Skip the gate on a default_on tool

mail_send_email pauses by default. In a controlled setup (sandbox mailbox only) you can turn it off:
With every gate removed this way, persistent_state: true is no longer required by the HITL compile guard.

Force a gate onto an MCP tool

MCP server tools have no AMAS source code to declare a requirement. Use the policy mandate:
For MCP servers added through Settings → Tools, AMAS already does this for you: on manifest discovery every newly-found tool that is not marked read-only by the server is added to the server’s approval set automatically. You only need mandate_human_approval for tools defined directly in YAML.

Separate instances, separate policies

The same tool can be gated for one agent and open for another, because hitl_config is per instance:

What the reviewer sees

When the gate trips, the run suspends and the client receives a HitlRequired event carrying everything needed to render the form: the tool name, its display name, the arguments the model proposed, and a schema for each parameter. The reviewer has three outcomes:
The tool runs. The reviewer may edit the arguments first — edited arguments are re-validated against the tool’s own schema, and a validation failure turns into an automatic decline rather than a bad call.
The tool is skipped. The reviewer can attach free-text feedback (“no, do it differently and this is how”), which is surfaced to the model as the reason for the decline so it can try another route.
Accept, and additionally pre-approve every further call of the same tool for the rest of the session. Only offered for the ratchetable layers — tool_default and instance. A mandatory tool or a policy mandate always pauses again, and the server rejects a session-scope approval sent for one anyway.

Parameter presentation

Tool authors can annotate parameters to keep the form readable. These are source-level, not YAML:

Non-interactive runs

A paused run needs someone to answer. Two consequences:
  • Webhook and scheduled triggers: a tool that always pauses can never complete in a trigger with no human attached. AMAS surfaces a warning on the trigger when the agent equips a mandatory HITL tool.
  • The /execute REST endpoint is request/response and runs the agent to completion. There is no resume step on it — approvals are answered over the WebSocket connection. See WebSocket API for the pause and resume messages.

Troubleshooting

Cause: a gated tool with persistent_state: false (the default).Fix: add persistent_state: true at the root of the configuration, or remove the gate from the tools listed in the error.
Cause: the tool declares hitl_requirement: mandatory, or a mandate_human_approval is also set. Both outrank the opt-out and ignore it silently.Fix: nothing to configure — a mandatory tool is designed to always pause. Use a different tool if the gate is unacceptable.
Cause: the tool’s own source declares default_on. Most write-type integration tools (Mail, TwentyCRM, Jira, Scheduler) do.Fix: set disable_human_approval: true on that tool instance if the gate is not wanted.
Error: hitl_session_busy — “This session is waiting for a human-in-the-loop response.”Cause: the session has an unresolved approval card. A session can hold only one open pause.Fix: accept or decline the open card before sending the next user message.
hitl_no_pending_pause — nothing is waiting; the pause was already resolved or the run ended.hitl_tool_call_mismatch — the tool_call_id sent does not match the open pause. Always echo back the id from the HitlRequired payload rather than caching one.hitl_resume_in_progress — a resume for this pause is already being applied. Do not retry on a timeout; wait for HitlResolved.

Next steps

WebSocket API

Pause and resume messages, and the full event protocol

Agent Capabilities

Tools, streaming and knowledge bases

YAML Configuration

Full configuration reference

Tools

Connect integrations and MCP servers