Human in the Loop

Human in the loop (HITL) lets a workflow pause and wait for a person before continuing. When a run reaches an agent 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 agent type. You attach a hitl_config block to an agent. The block has three modes:

ModeWhat the person doesBest for
conversationAnswers a question, picks a choice button, or types free textClarifying questions and approvals mid-conversation
formFills in a structured form you define with a JSON SchemaCollecting specific fields before the run continues
external_formCompletes a form hosted in an embedded iframeBringing 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: for form mode, the JSON Schema that defines the fields to collect.
  • timeout and timeout_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, or auto_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.

Form field types

Form fields are defined in form_schema using JSON Schema. Multi-select fields (array type) support three option sources, which can be combined:

  • Static options (items.enum): a fixed list of values baked into the schema. The field validates responses against this list.
  • Dynamic options (x-cascade): options loaded at render time from a configured lookup function. A multi-select field with only x-cascade and no items.enum renders as a multi-select without static validation; the valid values come from the cascade function at run time.
  • Both: a field can carry x-cascade for dynamic loading alongside items.enum for static validation.

Multi-select fields also support a default value. Set default to a JSON array of string IDs in the schema and the form renders with those items pre-selected. The platform also restores a previously submitted response as the default when a workflow-stored HITL response is available for the field.

{
  "type": "object",
  "properties": {
    "locations": {
      "type": "array",
      "items": { "type": "string" },
      "x-cascade": {
        "function": "getRef-locations",
        "function_id": "fn-loc",
        "display_field": "Name",
        "value_field": "Id",
        "depends_on": []
      },
      "default": ["loc-nyc"]
    }
  }
}

A scalar field with x-cascade remains a single-select; only array-typed fields render as multi-select.

The pause-and-resume cycle

Two distinct mechanisms can pause a run for a human. Both end in the same resume path.

Agent-level HITL

When the executor reaches an agent whose hitl_config requires a response, it:

  1. Creates a HITL request (with the prompt, the assignee, and the captured run state).
  2. Sets the step to paused and the run to paused.
  3. 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 agent 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.

The ask_human capability accepts a context field that controls what appears below the question. The field takes one of two forms:

  • Prose text: displayed under the question to give the assignee extra context.
  • JSON Schema object: when the value is a valid JSON Schema describing one or more fields, Autopilot renders a structured form instead of one-click choice chips. The person fills in the form fields and submits; the agent receives the answers as a JSON object it can parse.

If the value looks like JSON but cannot be decoded as a usable schema, it is dropped silently and the question renders on its own.

In both cases, resuming re-resolves any captured {{...}} references against the current run state, so the agent continues with correct, up-to-date data.

Nudge park mode

A notify_user agent configured as a Collect with no trigger_workflow_id pauses the run in park mode: the platform writes a durable nudge record, creates a HITL request (with request_type = 'nudge' and assigned_to_role = 'nudge_recipient'), and parks the run until a recipient taps the nudge card. This is a distinct HITL mechanism with its own rules:

  • The HITL request is written before the nudge, so the nudge is born with its correlation ID and every card carries it.
  • The collect target AI Employee (the one a card tap would start) must be published. A target with no published version is refused at publish time with a separate, actionable message.
  • The agent will be refused at run time if the nudge_operations_enabled feature flag is off for the workspace. Set trigger_workflow_id to start an AI Employee on the response instead, or use the Announce contract.
  • A park mode agent requires at least one downstream re-read agent (an agent, call_aie, or tool-bearing agent) in the subgraph after it. Without one, the run would wake and continue on pre-park values. The AI Employee builder surfaces this as a warning on all warning surfaces.
  • Tapping a nudge card in chat routes directly to the nudge response route, not the generic HITL respond endpoint.
  • A typed reply in a conversation that has a pending nudge button resolves to that nudge button, not as free text.
  • Parked nudge runs cannot be cancelled via POST /api/v1/workflow/hitl/{request_id}/cancel. That endpoint returns 409 for any pause whose request_type is nudge, because cancelling it there would hard-cancel the run and leave the nudge live with tappable cards that nothing could then settle. To end a parked nudge run, cancel the run itself.
  • Parked nudges expire through the HITL expiration scanner, not the nudge sweeper. When a parked nudge's expiry time passes, the scanner settles the nudge (recording any partial answers already collected) and drives the run to its timeout behavior.

Nudge reminders and expiry

Nudge agents (notify_user) that are not parked (that is, trigger-mode nudges waiting for a recipient to tap) are automatically managed by the nudge sweeper:

  • Reminders: the sweeper sends automatic reminder redeliveries on the configured cadence for all nudges, both parked and non-parked. Each reminder supersedes the previous card; recipients receive the same ask again without the builder needing to take any action.
  • Expiry: when a non-parked nudge's expiry time passes, the sweeper settles it automatically with outcome expired. The run continues according to its configured timeout behavior. Parked nudges (those pausing a run) are expired separately by the HITL expiration scanner, not the sweeper.

Reminders respect the nudge_operations_enabled flag for the workspace. If the flag is off, reminders are suppressed but expiry still runs. A settled nudge whose cards have not been repainted would otherwise leave live buttons visible to recipients.

Responding to a request

Assignees act on HITL requests through the in-app inbox or the API. Workflow viewers (not just editors and owners) can respond to HITL requests and cancel them.

EndpointPurpose
GET /api/v1/workflow/hitl/pendingList the requests waiting on you.
GET /api/v1/workflow/hitl/pending/for-meList nudges pending on the authenticated user.
GET /api/v1/workflow/hitl/pending/sent-by-meList nudges sent by the authenticated user.
GET /api/v1/workflow/hitl/{request_id}Read a single request.
POST /api/v1/workflow/hitl/{request_id}/claimClaim a role-assigned request so you can respond to it.
POST /api/v1/workflow/hitl/{request_id}/respondSubmit your response and resume the run.
POST /api/v1/workflow/hitl/{request_id}/cancelCancel a pending request. Not available for nudge parks (returns 409).

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.

Nudge cards and typed replies

Nudge cards (sent by a notify_user agent in Collect mode) can also be resolved by typing a reply in chat. A typed reply that matches a button label on the active nudge card resolves to that button's action, the same as tapping the button directly.

List cards and row actions

HITL requests can surface as list cards in supported channels. Each row in a list card can carry its own Edit and Cancel actions (row_actions) as an alternative to the card-level approve/reject buttons. List cards rendered in Microsoft Teams also support the following card-level fields:

FieldPurpose
listCardMultiSelectAllows selecting multiple rows before submitting.
listCardTotalDisplays a total or summary value on the card.
listCardPortalUrlProvides a link to an external portal for the full record.

Request status

Cancelled HITL requests display with a cancelled status badge in the run history dashboard. They are not reported as failures. The status badge uses localized labels for all states, including paused and cancelled.

Write-back failures on batch decisions

When an AI Employee records a decision on a Slack batch card but then cannot commit that decision to the source system (for example, a Coupa or NetSuite write fails inside the run), the card reflects the failure instead of showing a success indicator.

  • Decided items whose write-back failed are shown with a warning indicator rather than struck through. The card displays the short error from the source system next to the item, for example: ⚠️ Invoice — INV-20398 Approved recorded — NOT applied: Approval with that id was not found.
  • Items whose write-back succeeded continue to render normally.

The AI Employee reports write-back failures in-band. Mid-run, the next ask_human carries a writeback_failures map ({ choice_value: "short error" }). At the end of a run, failures are recovered from the decisions JSON in the run's final answer. Both paths update the card before it re-renders.

A write-back failure never erases the human's recorded decision. The decision ledger retains the writeback_status as audit evidence regardless of whether the card warning renders successfully.

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.

Nudge agent AI Employee builder warnings

When you configure a notify_user agent in the AI Employee builder, the AI Employee builder checks for common configuration problems and displays warnings inline.

nudge_collect_never_ends

The AI Employee builder displays this warning when a Collect nudge agent (trigger mode) has no expiry, no reminder cadence, and no close agent reachable downstream. A nudge in this state can only end when a person taps a button; no platform clock will ever settle it automatically.

To clear the warning, do any one of the following:

  • Set expires_after_seconds on the nudge agent to give it a deadline.
  • Add a reminder cadence so the platform re-prompts recipients.
  • Connect a notify_user close agent downstream so the workflow can settle the nudge explicitly.

This warning is advisory. A Collect nudge that recipients answer quickly is a valid design. The warning surfaces the risk that the nudge may stay live indefinitely if no one acts.

Where to go next

  • Workflows: where HITL fits in the run lifecycle.
  • Agents: attaching HITL to an agent and the ask_human capability.
  • API reference: the full HITL API.

Last updated: Oct 12, 2026