Skip to content

Human-in-the-Loop

The Human-in-the-Loop (HITL) pattern allows workflows to pause mid-execution, wait for human input or approval, and resume exactly where they left off without losing state or context.

flowchart TB
    Agent["Agent produces output"] --> Approval{"Approval Gate"}
    Approval --> |"Workflow pauses"| Wait(["⏸ Waiting for human"])
    Wait --> |"Human reviews"| Decision{Approved?}
    Decision --> |"Yes"| Resume["Workflow resumes"]
    Decision --> |"No"| Reject["Route to rejection node"]
    Resume --> Next["Next node continues"]

When a workflow reaches an approval node, it doesn’t just sleep — it completely halts execution and persists its state to a database with a waiting status.

This mechanism allows human operators to take as much time as needed to review the output, whether that’s minutes, hours, or days. Once the review is complete, the workflow is resumed with the human’s decision, allowing the orchestrator to route the graph accordingly.

  • High-stakes actions: An agent proposes a production deployment, financial transaction, or email blast, but a human must sign off before execution.
  • Content publication: A writer agent produces a draft article, and a human editor reviews and approves it before publishing.
  • Compliance & Auditing: Automated analysis that requires a mandatory human compliance review before proceeding.
  • Iterative feedback: A human provides specific, nuanced feedback during the pause, which is fed back to the agent for revision.

Instead of managing complex pausing logic, you simply add an approval node to your graph and route your agent to it.

id: review
type: approval
approval_config:
approval_type: human_review
prompt_message: Please review the draft and approve or reject.
review_keys: [draft]
timeout_ms: 86400000 # 24-hour timeout
read_keys: ['*']
write_keys: ['*']
SettingPurpose
prompt_messageInstructions shown to the human reviewer in your UI.
review_keysSpecific memory keys (like draft) the reviewer needs to examine to make their decision.
timeout_msHow long the workflow will wait in a paused state before automatically timing out.

When the workflow pauses, it emits a workflow:waiting event. Your application can listen for this event and alert the relevant human operator via Slack, email, or a dashboard. To continue, your application simply calls the orchestrator’s resume method with the human’s decision (e.g., approved or rejected) and any optional feedback. The GraphRunner will load the persisted state, inject the human’s decision, and traverse the appropriate outgoing edge.

Because human reviewers might forget to respond, the timeout_ms property ensures workflows don’t sit in a waiting state infinitely. The orchestrator will surface this as a timeout error if the threshold is breached, allowing you to clean up or alert a fallback reviewer.