Human in the Loop
Pause a workflow for a person to review, then approve and resume or reject
The Human in the Loop block pauses a running workflow and waits for a person to make a decision. The reviewer sees the workflow's output so far, then either approves (the workflow resumes) or rejects (the workflow stops). Use it for sign-offs, approvals, and any step where a human must decide before automation continues.
A paused workflow waits indefinitely until a reviewer acts — there is no automatic timeout. Reviewers are notified and can open the review from their notifications.
When to use it
- Approvals — a refund, a contract, a publish step that needs sign-off.
- Quality gates — a person checks an AI-generated draft before it goes out.
- Compliance — a required human decision is recorded before the process continues.
What the author configures
| Setting | What it does |
|---|---|
| Review Question | The question shown at the top of the review panel. Supports variables like <blockName.field>, resolved to the workflow's real values when it pauses. |
| Paused Output (Context) | The fields the reviewer sees — the workflow output they need to decide. Reference upstream blocks with <blockName.field>. |
| Resume Form Fields | Optional typed inputs the reviewer fills in (text, number, boolean, …). Each becomes available to downstream blocks after resume. |
| Decision Options | Optional. Replace Approve / Reject with your own choices — Go / No go / Defer, a severity call — each mapped to an outcome. See Decision options. |
| Approver Notifications | Optional notifications (e.g. Slack, email) sent when the workflow pauses. |
| Reviewer Role | Who may resolve the pause — an organization role, admin or member. Nothing else is accepted: to name specific people, choose them as approvers instead. |
| Narrow reviewers at runtime | Off by default. When on, an earlier block chooses which of the declared approvers are asked. See Choosing reviewers at runtime. |
| Runtime reviewer selection | Shown when narrowing is on. A reference such as <pick_reviewers.result> to an upstream output that is a list of user ids. |
| Resolution | First claim (any one reviewer decides), All must approve, or Quorum (N reviewers). |
Choosing reviewers at runtime
Sometimes the right reviewers are only known once the workflow runs: the experts for this contract's jurisdiction, or the owners of the affected system. Compute the list in an earlier block (a Function block, an Agent, an API call) and hand it to the Human in the Loop block:
- Declare the pool as usual: Specific Approvers, or a Reviewer Role (
adminormember). This is the widest set of people who may ever review this step. - Turn on Narrow reviewers at runtime.
- Set Runtime reviewer selection to the upstream output, for example
<pick_reviewers.result>. The value must be a list of user ids, such as["user_123", "user_456"].
When the block pauses, exactly the selected people are notified, and only they can resolve it.
The selection can only narrow the pool. The block fails with an explanatory error, and nobody is notified, when:
- a selected user is not in the declared pool. A runtime value can never add an approver the template did not declare;
- the selection names the run's own user or the user who started the process instance. A run may not choose itself as its approver;
- a selected user is not a member of the organization;
- the selection is empty or malformed: an unresolved reference, a single id instead of a list, an object, or more than 50 ids. There is no fallback to the whole pool; fix the upstream block instead.
If organization membership cannot be verified at that moment, the pause is retried rather than failed.
Approvals required is capped at the number of people actually selected, so a gate that asks for three sign-offs and receives two reviewers releases once both approve. It never hangs.
Every selection and every refusal is recorded in the organization's audit log (workflow.hitl.reviewers_selected and workflow.hitl.reviewer_selection_refused). Each entry lists the pool, the chosen reviewers and the run identities that were excluded. If the selection cannot be recorded, the block does not pause.
Declared approvers (without runtime narrowing) are also checked for organization membership when the block pauses. A named approver who has left the organization fails the block instead of receiving a review they can no longer open.
What the reviewer sees
When a reviewer opens a paused workflow, the review panel shows, in order:
Status — Paused, Queued, Resuming, Resumed, or Rejected.
AI advisory (when an LLM integration is configured) — a short, grounded decision brief that summarizes what to weigh, drawn from the workflow's paused output. Expand Reasoning details to see how the AI weighed the information.
The advisory is decision support only — it never states "approve" or "reject", and it never decides for you. You make the decision. It is grounded in the paused output the workflow surfaced; if there is nothing to ground on, no advisory is shown.
Review question and paused output — the author's question (or a one-line summary) and the context table of workflow values to review.
Resume form — any inputs the author asked the reviewer to fill in.
Approve or Reject — Resume Execution continues the workflow; Reject stops it. When the author declared decision options, the panel shows one button per option instead, with an optional note.
Approve vs. reject
- Approve / Resume — the workflow continues from where it paused. Any resume-form values are passed to downstream blocks.
- Reject — the gate refuses. When the refusal settles the gate (see below), the workflow stops and cannot be continued: the status becomes
Rejectedand the workflow will not resume. You can add an optional reason, which is recorded with the decision.
When does one rejection stop the workflow?
It depends on the approval rule, and the answer is the plain reading of the rule you chose:
| Approval rule | One rejection |
|---|---|
| All approvers must approve | Stops the workflow immediately. Everyone had to approve, so one refusal is the answer — the remaining approvers are not asked to vote on a settled question. |
| A set number must approve (2 or more) | Does not stop it. "2 of 5 must approve" is not answered by one refusal, so the gate keeps collecting. The refusal is recorded and counts toward neither the approvals nor the release. |
| A set number must approve = 1 | Stops the workflow. The first response decides, whichever way it goes. |
Until this changed, a single rejection stopped every kind of gate, including a quorum — so a five-person panel could be resolved by one dissenting reviewer while the recorded tally still showed four approvals. If you want one refusal to be decisive, choose All approvers must approve.
A quorum gate also closes when the approvals it needs can no longer arrive. With 2 of 3 required and two reviewers rejecting, one approval is the ceiling — so the gate resolves as rejected at that point rather than waiting for a third answer that cannot change the result. The reviewer who had not answered is told the review closed, exactly as when a quorum is reached early.
Rejection is final. Once a pause is rejected, that workflow run is over — re-opening the review will not let you resume it. If a workflow should be able to continue down a different path instead of stopping, declare a decision option with the outcome other and route on it, rather than Reject.
Decision options
Approve / Reject answers a yes-or-no question. Many decisions are not one: go, no go or defer; low, medium or high severity. Decision Options lets you declare the choices, and each one says how the gate should count it:
| Outcome | How the gate counts it |
|---|---|
approve | A sign-off. Counts toward Approvals required. |
reject | A refusal — exactly like the Reject button, including when it stops the workflow (see above). |
other | Neither. It never counts as a sign-off, and it does not stop the workflow. |
Each option is a row with three columns:
| Column | What to put in it |
|---|---|
| Value | What downstream blocks see. Letters, digits, _ and - only (go, no_go, defer). |
| Label | The button text the reviewer sees. |
| Outcome | approve, reject, or other. |
Declare two or more options — a single option is not a choice. There are no
weights: every reviewer's vote counts once. Deploying a workflow with an invalid
option (a single option, a bad value, an unknown outcome, a duplicate) fails
with a message naming the option; a ballot with no approve option is allowed
but flagged, because that gate can never be approved. Leave the table empty to
keep plain Approve / Reject.
How a decision with options is released
- Approvals required = 1 — the first vote decides. An
othervote releases the gate with the verdictundecided, and the workflow continues. - All approvers must approve — a
rejectvote settles it as a refusal, and anothervote settles it asundecided: either way, not everyone approved. - A set number must approve (2 or more) — only
approvevotes count toward the number. When enoughrejectandothervotes have arrived that the number can no longer be reached, the gate closes: as rejected if anyone chose arejectoption (the workflow stops), otherwise as undecided (the workflow continues).
Route on the result with a Condition or Router block: <gate.verdict> is
approve, reject or undecided, and <gate.aggregate.byOption.defer> is the
number of reviewers who picked Defer — 0 when nobody did.
With decision options declared, the gate only accepts a vote for one of them.
A submission without an option, or with one that was never declared, is
refused (HTTP 400, allowed lists the values) and is not recorded.
Who the reviewers are
The Reviewer Role is always an organization role, admin or member. It cannot name a custom process-flow persona. A persona can be assigned by the running workflow itself, so a gate that reviewed as one would let the workflow choose who approves it. A gate whose reviewer role is anything else is refused when the extension is built or imported, when the workflow is deployed, and again when the gate runs.
Outside a process flow, the reviewers are the members of that organization role.
Inside a process flow, reviewers resolve in this order:
- the people assigned to a persona of the same name (
adminormember) on this process-flow instance; - otherwise, the members of the organization role itself.
The organization's administrators are not a fallback here. Reviewers are the people allowed to resume the workflow, and that is approval authority, which only a persona holder gives. If nobody resolves, the pause fails with a configuration error instead of waiting on an empty reviewer list. Assign someone to the persona, then re-run the workflow.
Process-flow notifications go further: when nobody holds the persona, they reach the organization's administrators so that someone staffs it. A distribution sends those administrators a staffing notice only, never its content, and is sent to the persona's holder once someone is assigned. In both cases the administrators are told, and they cannot approve in the persona's place.
When a reviewer is checked
A reviewer role that is not an organization role is refused before the gate opens. The check runs at each of these points:
| When | What happens |
|---|---|
Extension build and extension validate | A reviewer role that is not admin or member fails the build (hitl_reviewer_not_org_role), whether or not a process flow in the extension declares a persona of that name. |
| Extension import | The same check, and it also refuses the import. An extension built before this check existed is refused until its gate is fixed and the extension is rebuilt. |
Deploy — from the canvas, Monitor, an automation, a promotion to another environment, or POST /api/v1/workflows/{id}/deploy | A gate that is wrong whoever runs it refuses the deploy, and the error names the gate. That covers a resume form field with a reserved name, decision options that cannot be voted on, and a reviewer role that is not an organization role. A rollback to an earlier version is never refused: it restores a version that already ran, so its findings come back as warnings (hitlWarnings in the API response). |
| Opening a process-flow instance | The instance page lists the reviewer personas of the gates in the workflows it runs that nobody holds yet. It reads them from the deployed versions of those workflows, which are the ones that run. |
| When the gate pauses | A reviewer role that is not an organization role fails the run, even in a version deployed before this check existed; nobody is notified. If an organization role resolves to nobody, the run fails too. The error names the gate, the reviewer, and what each step of the order above found. |
An organization role is only admin or member. The retired owner role matches nobody.
Opening a review
Reviewers receive a notification when a workflow pauses. Opening the notification takes you straight to the workflow with the review panel open, where you can read the context and decide.
Multiple reviewers
With All must approve or Quorum, the workflow stays paused until enough reviewers have submitted. The AI advisory is generated once per pause and shared across all reviewers — everyone sees the same brief.
Only the gate's reviewers count toward its threshold. Someone with edit access to the workflow who is not a reviewer — typically its owner — can still submit; that answer is kept in responses, but it is not one of the reviewers' answers: it does not count as an approval, does not use up a reviewer's place in a quorum, and does not end an All must approve gate on its own. With a single sign-off (First claim, or Approvals required = 1) any authorized answer settles the gate, as before.
When a quorum closes early
A Quorum gate releases as soon as the required number of reviewers respond — that is the point of it. Reviewers who had not yet responded are notified that the review closed, so nobody discovers it by trying to submit.
If a response arrives after the gate has closed, it is recorded, not discarded. The reviewer is told the review had already closed and that their answer did not change the outcome, and the answer is kept on the paused-execution record alongside the responses that did count. Late responses never alter the released decision or restart the workflow.
If late responses keep arriving on a gate, its Approvals required count is probably lower than the decision warrants — the late answers are the signal to raise it.
What downstream blocks can read
Every released gate carries the same outputs, at every approval rule:
| Output | What it is |
|---|---|
verdict | approve, reject, or undecided. Prefer this for routing. |
decision | approved, rejected, or undecided — the same thing as a sentence. |
approved | true / false. Absent when the verdict is undecided. |
responses | Every submission, in order — including any from someone who is not a reviewer. |
aggregate.approvedCount · rejectedCount · otherCount · totalResponses | The reviewers' tally. otherCount is the number of other-outcome votes. |
aggregate.byOption.<value> | With decision options: the number of votes per option. Every declared option is present, including those nobody picked (0). |
option | With decision options and Approvals required = 1: the value the reviewer chose. |
reason | The refusal reason — only on a gate that refused. |
responses and aggregate used to appear only when more than one sign-off was
required, so a multi-reviewer design that set Approvals required = 1 kept
only the first answer and silently lost the rest. They are now always present.
Branch on verdict or decision, not on approved. approved is absent on
an undecided gate, and <gate.approved> === false does not match an
absent value — a Condition written that way will not take either branch.
Reserved resume-form field names
These names belong to the block's own output and are overwritten at runtime, so a resume-form field must not use one:
approved · decision · reason · verdict · responses · aggregate ·
submission · response · url · option
A field named approved is the sharpest case: submitting the review always
writes approved: true over it, so a reviewer who chose "No" would have their
answer recorded as an approval. If you need a richer vote than approve/reject,
declare decision options — the gate then counts the vote
itself, instead of a form field the release rule cannot see.