Human in the Loop
Human in the loop (HITL) lets a workflow pause and wait for a person before continuing. When a run reaches a node configured for HITL, the platform creates a request, assigns it to a user or a role, pauses the run, and notifies the assignee. The run resumes — picking up exactly where it left off — once that person responds. Use it for approvals on a sensitive action, for collecting structured input mid-run, or for asking a clarifying question.
HITL is first-class in the run model: a paused run has the paused status, and the waiting step has the paused step status, so paused work is fully visible in the run history.
How HITL is configured
HITL is not a separate node type. You attach a hitl_config block to an agent node. The block has three modes:
| Mode | What the person does | Best for |
|---|---|---|
conversation | Answers a question, picks a choice button, or types free text | Clarifying questions and approvals mid-conversation |
form | Fills in a structured form you define with a JSON Schema | Collecting specific fields before the run continues |
external_form | Completes a form hosted in an embedded iframe | Bringing an external system's form into the flow |
A minimal conversation-mode block looks like this:
{
"mode": "conversation",
"prompt": "This refund exceeds $500. Approve?",
"assign_to": "triggering_user",
"timeout": "1h",
"timeout_behavior": "fail_run",
"channels": ["in_app"]
}
Assigning the request
assign_to decides who can respond. It is always required and takes one of three forms:
"triggering_user"— the user who started the run. Resolved to that user's ID at run time.- A user UUID — a specific, named user. Validated at publish time, so a non-existent user fails the publish with
HITL_INVALID_ASSIGNEE. "role:<name>"— anyone holding that role (for example"role:builder_admin"). The request is then claimable: an eligible user claims it before responding, so two people don't act on the same request.
Other settings
prompt— the question or instruction shown to the assignee.form_schema— forformmode, the JSON Schema that defines the fields to collect.timeoutandtimeout_behavior— how long to wait (a Go duration string such as"15m"or"1h") and what to do if no one responds in time:fail_run,skip_node, orauto_approve.channels— where to notify the assignee; defaults to["in_app"].max_hitl_interactions— caps the number of back-and-forth rounds for a conversation request (default 5).summarize_on_exit— when true, the agent summarizes the HITL exchange before the run continues.
The pause-and-resume cycle
Two distinct mechanisms can pause a run for a human. Both end in the same resume path.
Node-level HITL
When the executor reaches an agent node whose hitl_config requires a response, it:
- Creates a HITL request (with the prompt, the assignee, and the captured run state).
- Sets the step to
pausedand the run topaused. - Notifies the assignee on the configured channels.
The run stays paused — consuming no compute — until a response arrives. Other independent branches of the DAG that don't depend on the paused node keep running in parallel.
Mid-execution ask_human
An agent can also pause itself mid-reasoning by calling the built-in ask_human capability — for example, when it realizes it needs a missing detail to proceed. The agent serializes its state, the workflow creates a HITL request, and the run pauses. When the person answers, the response is folded back into the agent's context and the same agent resumes its reasoning. Multiple ask_human rounds are supported within one step.
In both cases, resuming re-resolves any captured {{...}} references against the current run state, so the agent continues with correct, up-to-date data.
Responding to a request
Assignees act on HITL requests through the in-app inbox or the API:
| Endpoint | Purpose |
|---|---|
GET /api/v1/workflow/hitl/pending | List the requests waiting on you. |
GET /api/v1/workflow/hitl/{request_id} | Read a single request. |
POST /api/v1/workflow/hitl/{request_id}/claim | Claim a role-assigned request so you can respond to it. |
POST /api/v1/workflow/hitl/{request_id}/respond | Submit your response and resume the run. |
POST /api/v1/workflow/hitl/{request_id}/cancel | Cancel a pending request. |
The shape of the response depends on the mode. A conversation request expects a choice or free-text answer:
POST /api/v1/workflow/hitl/{request_id}/respond
Content-Type: application/json
{ "response": { "type": "freetext", "value": "Approved — proceed with the refund." } }
A choice response sends { "type": "choice", "value": "<one of the offered choices>" }. Form requests submit the collected field values. On a valid response, the run leaves paused, the step completes, and execution continues down the DAG.
Plan for the wait. A paused run can sit for as long as your timeout allows. Set timeout_behavior deliberately: auto_approve keeps throughput high for low-risk steps, while fail_run is the safe default for anything that must not proceed without a human.
Where to go next
- Workflows — where HITL fits in the run lifecycle.
- Agents — attaching HITL to an agent node and the
ask_humancapability. - API reference — the full HITL API.