Designing Human-in-the-Loop Forms

Human-in-the-loop (HITL) pauses an agent mid-run so a person can answer a question, fill in a form, or approve a step. The agent collects what it needs, the run resumes with that input, and the agent continues. This guide covers the three HITL modes (conversation, inline form, and external form) and how to design each one.

HITL is configured per agent, under Advanced settings, on the LLM-driven agent types (intent classification, search and respond, extraction, rule validation, respond, document agent, and custom).

How HITL works

When HITL is enabled, the agent gets an ask_human capability. As it reasons through a run, the agent decides when it needs human input and pauses. The platform records the pause as a first-class request, notifies the assigned person, and waits. When the person responds, the agent resumes with their answer folded into its context and can pause again if it needs more. Multiple rounds per step are supported.

Because HITL is driven by the agent rather than a fixed step in the graph, you control what to collect and when through the HITL configuration and your Instructions, not by adding an agent.

On the dashboard, the Review button appears automatically when a row transitions to paused state awaiting HITL input. You do not need to reload the page.

Enabling HITL

  1. Open the agent in the AI Employee builder and expand Advanced settings.
  2. Turn on the Human in the loop toggle.
  3. Choose a Mode. The modes available depend on the AI Employee's interaction type:
    • Chat (and most builds): Conversation, Form, or External form.
    • Dashboard: Form or External form (no free-form conversation).
    • Voice (Beta): Conversation only (a phone caller can't fill a visual form).

Mode 1: Conversation

The simplest mode. The agent asks the person a question in natural language and waits for a free-text reply.

  1. Set the mode to Conversation.
  2. In HITL Instructions, tell the agent what information to collect and when it has enough to proceed, for example: "Ask the user for their order number and a description of the issue before continuing. If they don't have an order number, ask for the email used at checkout."

The agent can present the question as free text, and may offer a short list of choice buttons when the answer space is small and mutually exclusive. The person can always type a free-text answer even when buttons are shown.

Use conversation mode when the input is open-ended or conversational, or when you're building for voice.

Mode 2: Inline form

A structured form rendered in the chat or dashboard UI. Use this when you need several named fields at once.

  1. Set the mode to Form.
  2. Choose Add Form to open the form editor.
  3. Give the form a Title (required) and an optional Description and Submit label.
  4. Add fields with Add field. For each field set:
    • Name — the key returned to the agent. Names must be unique within the form.
    • Type — see the field types below.
    • Label and Placeholder — what the person sees.
    • Required — whether the field must be filled.
    • For option-based types, the Options (each a value plus a label).
    • For number fields, an optional Min and Max.
  5. Save the form.

You can attach more than one inline form to an agent; the agent picks the right form for the situation based on its Instructions.

Field name constraints

Field names are returned to the agent as opaque string keys exactly as you enter them. However, names containing ., [, ], ", ', or | are treated as opaque keys throughout the form (they are not interpreted as nested paths). The submitted payload always uses the original name as the key, regardless of which characters it contains.

Inline field types

TypeRenders asNotes
textSingle-line text input-
emailEmail input-
telPhone input-
numberNumber inputSupports Min / Max.
dateDate picker-
textareaMulti-line text area-
selectDropdownRequires options.
radioRadio groupRequires options.
checkboxCheckbox-
card_selectSelectable cardsRequires options.
slot_selectTime-slot pickerRequires options.

When the person submits, each field's value is returned to the agent keyed by its Name, and the run resumes.

Cascade (lookup) dropdowns

Some dropdown fields are populated at run time by calling an integration, called a cascade or lookup dropdown. When the lookup fails, the form stays open so the person can retry without losing values already entered.

How errors surface depends on why the lookup failed:

  • Integration business-rule error (4xx): If the integration returns an ApplicationStatusCode in the 4xx range along with a Message, that message is displayed inline next to the dropdown field in both the Teams and web-app HITL forms. For example, "You already have a booking during the selected time. Please choose a different date or time." The person sees the exact reason and can act on it.
  • Plumbing failure (5xx or other): A generic fallback message is shown instead. Internal error details are never surfaced to the person.

In Teams forms, when a lookup has failed and a retry button is shown, the initial "Tap Load to fetch options" prompt is suppressed to avoid conflicting with the retry affordance.

Picker fields and typed lookup inputs

When a picker field (such as a dropdown populated by an integration function) has fixed inputs declared by an option spec (for example, page and per_page constants for a paged lookup), those inputs are split from field-derived inputs at form-build time:

  • input_map holds references to other form fields (values the user fills in). These values are always strings.
  • input_values holds the fixed constants the spec declared. The platform coerces each constant to the type the lookup's own JSON Schema declares for that parameter, so an integer parameter receives a number, a boolean parameter receives true/false, and so on. String parameters stay as text.

A field-derived value always wins on a key clash: if the same parameter appears in both input_map and input_values, the form field's value is used. Fixed inputs are never listed in depends_on and never block a cascade from resolving.

For cascading picker fields, the typed fixed inputs ride along in resolved_input on the cascade config so the browser forwards them with the runtime lookup call.

Mode 3: External form

Embeds a form hosted at a URL (in an iframe) instead of rendering native fields. Use this when the form already exists in another system, or needs logic the inline builder can't express.

  1. Set the mode to External form.
  2. Choose Add External Form and configure:
    • Display name (required) and optional Description.
    • URL — where the form is hosted.
    • Button label / Cancel label and optional Max width / Max height for the embedded frame.
    • Data schema (JSON) — an optional JSON Schema describing the small metadata the agent should pass when it opens the form (for example, flowType, patientId). The platform fetches large data separately, so keep this to identifiers and flags.
    • Data sources — integration functions the platform calls when the form opens, whose results are injected into the form. Each source has a function id, an output key, and an input mapping (map form-data keys to function parameters; prefix a value with = to pass a literal).
    • Post-submit function — optionally name an integration function to run deterministically when the form is submitted, instead of letting the agent decide what to do with the submission. The named function must be one of the agent's selected Tools. Leave it blank to keep the agent-driven path.

Use the post-submit function when submitting the form should always do one fixed thing (for example, "submit timesheet"). It removes the model from the decision and guarantees the action runs.

Approval form dropdowns

Approval forms (rendered when an ask_human step presents a parameter with a fixed option list) show a dropdown for any parameter whose JSON Schema type is string, integer, number, or boolean and that declares an enum array. Previously only string enums rendered as dropdowns; numeric and boolean enums now do as well.

When the form submits, each dropdown value is coerced to its declared type: an integer enum submits a number, a boolean enum submits true or false (not the string "true"), and a string enum submits a string.

If the option list is temporarily empty (for example, while an async lookup is still loading), the dropdown preserves any value the agent already chose by showing it as the sole available option. A required dropdown with no options does not block submission, because there is nothing to pick; the chosen value is forwarded as-is.

Nudge agents in workflows

Workflows can send nudges (structured cards delivered over Teams) using notify_user agents. A nudge agent in Collect mode waits for one or more people to respond before the follow-on AI Employee starts. You configure a nudge agent's behavior through its operation setting: send, update, or close.

Operations

  • send — mints a new nudge and delivers the card to recipients. In Collect mode with a trigger_workflow_id, the sending step completes immediately and the response starts the named AI Employee.
  • update — revises the content of an existing nudge and re-delivers the card. Pass the nudge's id via nudge_id in the input mapping. If the nudge has already been answered or settled by the time the update runs, the step completes with a refused_reason on its output rather than failing. This is a normal race the builder cannot prevent.
  • close — programmatically settles an open nudge. Pass the nudge's id via nudge_id in the input mapping. The step's output reports the nudge's final verdict as outcome.

Collect mode and the "nudge can never end" warning

A trigger-mode Collect nudge completes its sending step at send. The nudge itself then lives until something settles it. If nothing can settle it, the card stays tappable indefinitely.

The AI Employee builder shows a warning: "Nudge has no expiry, no reminder cadence and no close node downstream. It can only end when a person acts. Add an expiry or a close node below it." This warning appears on any Collect nudge that has none of the following:

  • An expiry (expires_after_seconds set to a positive value).
  • A reminder cadence (a reminders block with a positive cadence amount).
  • A close agent reachable downstream in the workflow.

Any one of the three clears the warning. The warning is advisory: a Collect nudge that someone answers promptly is valid to build. It does not block publishing.

Park mode (a Collect nudge with no trigger_workflow_id) is exempt from this warning because the platform already refuses to publish a park with no positive expiry. The warning only applies to trigger-mode Collect nudges.

To resolve the warning, do one of the following:

  1. Set expires_after_seconds on the nudge agent to a positive number of seconds.
  2. Add a notify_user agent with operation: close somewhere downstream of the nudge, and wire the nudge's id to its nudge_id input.

List cards with per-row actions

When a nudge's form_schema includes a list of items, you can add per-row action buttons (such as Edit or Cancel) instead of a single approve/reject response at the card level. Set the row_actions field inside form_schema to define the per-row buttons.

Two additional fields support list card rendering:

  • list_card_portal_url — a URL shown in the card's overflow footer, for cases where the list is too long to display in full.
  • list_card_total — the total item count to display when the card shows a subset of a larger list. Also controls Teams single-vs-batch send mode.

Assigning the request

Each HITL pause is assigned to a person to respond. The assignee resolves at run time, typically to the user who triggered the run or to a specific user you configure. Publishing is blocked if a HITL configuration references an invalid assignee, so the request always lands on a real person.

Batch approval cards and write-back failures

When an ask_human step uses multi_select (a batch approval checklist), the AI Employee commits each decision to the source system after the person acts. If that write fails, the card distinguishes the failed item visually: instead of showing the item as struck through with a success mark, it renders a warning line — ⚠️ <label> Approved/Rejected recorded — NOT applied: <source-system error> — so the approver can see that their decision was recorded but not applied to the source system.

The AI Employee reports failures in-band using the writeback_failures field on the next ask_human call (a map of choice value to short error string). At the end of a run, when no further ask exists, the platform mines the same information from the decisions JSON the AI Employee emits as audit evidence. Both paths feed the card re-render automatically; you do not need to configure anything extra.

A write-back failure never erases the person's decision. The decision is always recorded; only the commit to the source system failed. The run's decision ledger carries writeback_status as audit evidence regardless of the card rendering.

If you are designing an AI Employee that commits decisions to an external system during a batch approval flow, instruct it to report any write failures in the writeback_failures map on its next ask_human call, including every value whose write failed and a short description of the error from the source system.

Designing reliable HITL

  • Keep questions specific. "What is the order number?" beats "Can you give me more details?"
  • Tell the agent when to stop. In conversation mode, state the stopping condition explicitly so the agent doesn't loop ("Once you have the order number and the issue description, proceed").
  • Prefer forms for structured data. If you need three named fields, a form collects them in one round instead of three back-and-forth turns.
  • Mark only truly required fields. Required fields block submission; optional fields let the person move on.
  • Match the channel. Forms render in chat and dashboards; voice callers get conversation mode. Don't design a form for a phone-only AI Employee.
  • Give Collect nudges a termination path. If you use a Collect nudge in trigger mode (where the response starts another AI Employee), the AI Employee builder will warn you when the nudge has no expiry, no reminder cadence, and no close agent downstream. A nudge in that state can only end when a person acts. To clear the warning, add an expiry duration or place a close agent below the Collect agent.

What happens on resume

When the person responds, the agent receives their input and continues from where it paused, re-resolving any templated values and incorporating the answer. The run's step trace records the pause and the response, so you can see exactly what was asked and answered in the run history.

What's next

Last updated: Oct 12, 2026