Skip to main content
Not every workflow should run end-to-end without a human checkpoint. You might need a content reviewer to approve a draft before it gets published, a manager to sign off on an automated decision, or a developer to verify a generated migration script before it runs. dagraph provides two node types for human-in-the-loop control: approval_gate pauses the run until someone explicitly approves or rejects, and user_input pauses to collect a free-form response. This guide covers both.

The approval_gate node

An approval_gate node pauses the entire run at that point. The workflow writes all upstream artifacts, then exits with status paused. No downstream nodes execute until a human approves (or rejects) the gate via the CLI. Here is the complete approval_flow.yaml example — an AI drafts an announcement, a human reviews it, and a second agent condenses it into a social post only after approval:
name: draft_with_human_review
description: |
  Generate a short announcement, pause for human approval, then produce
  a social-media-ready summary once approved.

budget:
  max_tokens: 10000
  max_usd: 1.00

nodes:
  - id: draft
    type: agent
    model: claude-haiku-4-5-20251001
    max_output_tokens: 600
    prompt: |
      Write a short internal announcement (3–4 sentences) about the launch
      of a feature called "{{ feature }}". Tone: clear, non-hype.

  - id: review
    type: approval_gate
    depends_on: [draft]
    timeout_seconds: 86400  # gate expires after 24 hours if not acted on
    prompt_to_human: |
      Review the draft announcement above in the artifact store.
      Inspect it with:   agentgraph inspect <run_id> --node draft
      Approve with:      agentgraph approve <run_id> review
      Reject with:       agentgraph approve <run_id> review --reject --comment "why"

  - id: social_post
    type: agent
    model: claude-haiku-4-5-20251001
    max_output_tokens: 400
    depends_on: [draft, review]   # needs both the draft text and the approval record
    prompt: |
      The following draft was approved for external publication:

      {{ draft }}

      Condense it into a single social-media post (≤280 characters).
      Keep the key information, drop internal jargon.

Full approval walkthrough

1

Start the run

Run the workflow with your input. The DAG executes the draft node, then pauses at review.
agentgraph run approval_flow.yaml --input feature="Dark mode"
The CLI prints the run ID and exits, noting the gate is awaiting approval:
Run abc123def456 paused — 1 approval gate(s) awaiting human decision.
• review since 2026-04-26T10:00:00Z

Approve:  agentgraph approve abc123def456 review
Reject:   agentgraph approve abc123def456 review --reject --comment "<reason>"
Resume:   agentgraph run approval_flow.yaml --run-id abc123def456
2

Inspect the draft

Read the upstream artifact before deciding:
agentgraph inspect abc123def456 --node draft
This prints the full text of the draft node’s output so you can review it before approving.
3

Approve or reject

To approve the draft:
agentgraph approve abc123def456 review
To reject with a reason:
agentgraph approve abc123def456 review --reject --comment "needs a stronger CTA"
A rejection sets the review node to failed, which also fails any downstream nodes that depend on it — including social_post. The run ends at that point. Fix and re-run with a new run ID to start fresh.
4

Resume the run

Once approved, resume execution from where it paused. dagraph skips all already-completed nodes and picks up at social_post:
agentgraph run approval_flow.yaml --run-id abc123def456
You can also use the shorthand resume command:
agentgraph resume approval_flow.yaml abc123def456

Approval decision as a template variable

When a gate is approved, dagraph writes a JSON artifact to the review node containing the decision metadata. Downstream nodes can reference this via {{ review }}:
{
  "decision": "approve",
  "approver": "alice",
  "timestamp": "2026-04-26T10:15:30Z",
  "comment": "",
  "awaiting_since": "2026-04-26T10:00:00Z"
}
Use this in a downstream prompt to include reviewer context:
- id: final_post
  type: agent
  depends_on: [draft, review]
  prompt: |
    Approved by {{ review | fromjson | attr('approver') }} at
    {{ review | fromjson | attr('timestamp') }}.

    Draft: {{ draft }}

    Produce the final version.

Gate timeouts

Set timeout_seconds on any approval_gate to automatically fail the gate if it hasn’t been acted on within the window. This prevents workflows from sitting paused indefinitely in production:
- id: review
  type: approval_gate
  depends_on: [draft]
  timeout_seconds: 3600   # fail if not approved within 1 hour
When the scheduler resumes a run and discovers the gate has expired, it marks the gate as failed and the run ends.

Collecting free-form input with user_input

Use a user_input node when you want to inject a human-provided value into the DAG rather than a binary approve/reject decision. The workflow pauses until someone responds via the CLI.
nodes:
  - id: ask_audience
    type: user_input
    prompt_to_human: "Which target audience should the post address?"
    input_type: select
    options: [developers, executives, general-public]
    timeout_seconds: 7200

  - id: write_post
    type: agent
    depends_on: [ask_audience]
    model: claude-haiku-4-5-20251001
    prompt: |
      Write a product announcement for {{ ask_audience }}.
input_type accepts three values:
TypeDescription
textAny string
selectOne of the values in options
confirmA yes/no boolean (yes, no, true, false, y, n)
Respond to a paused user_input node:
# For a select or text node
agentgraph respond abc123def456 ask_audience developers

# Pass --dag to validate the value against the declared options
agentgraph respond abc123def456 ask_audience developers --dag approval_flow.yaml
Then resume the run as normal:
agentgraph run approval_flow.yaml --run-id abc123def456

Parallel agents

Build fan-out/fan-in DAGs that run multiple agents simultaneously.

Scheduling and webhooks

Trigger approval-gated workflows on a schedule or via HTTP webhook.