> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dagraph.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Add human approval gates to your dagraph workflows

> Pause a dagraph workflow so a human can inspect artifacts, approve or reject the result, then resume execution — with full CLI commands for each step.

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:

```yaml theme={null}
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

<Steps>
  <Step title="Start the run">
    Run the workflow with your input. The DAG executes the `draft` node, then pauses at `review`.

    ```bash theme={null}
    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
    ```
  </Step>

  <Step title="Inspect the draft">
    Read the upstream artifact before deciding:

    ```bash theme={null}
    agentgraph inspect abc123def456 --node draft
    ```

    This prints the full text of the `draft` node's output so you can review it before approving.
  </Step>

  <Step title="Approve or reject">
    To approve the draft:

    ```bash theme={null}
    agentgraph approve abc123def456 review
    ```

    To reject with a reason:

    ```bash theme={null}
    agentgraph approve abc123def456 review --reject --comment "needs a stronger CTA"
    ```

    <Note>
      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.
    </Note>
  </Step>

  <Step title="Resume the run">
    Once approved, resume execution from where it paused. dagraph skips all already-completed nodes and picks up at `social_post`:

    ```bash theme={null}
    agentgraph run approval_flow.yaml --run-id abc123def456
    ```

    You can also use the shorthand `resume` command:

    ```bash theme={null}
    agentgraph resume approval_flow.yaml abc123def456
    ```
  </Step>
</Steps>

## 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 }}`:

```json theme={null}
{
  "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:

```yaml theme={null}
- 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:

```yaml theme={null}
- 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.

```yaml theme={null}
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:

| Type      | Description                                               |
| --------- | --------------------------------------------------------- |
| `text`    | Any string                                                |
| `select`  | One of the values in `options`                            |
| `confirm` | A yes/no boolean (`yes`, `no`, `true`, `false`, `y`, `n`) |

Respond to a paused `user_input` node:

```bash theme={null}
# 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:

```bash theme={null}
agentgraph run approval_flow.yaml --run-id abc123def456
```

<CardGroup cols={2}>
  <Card title="Parallel agents" icon="arrows-split-up-and-left" href="/guides/parallel-agents">
    Build fan-out/fan-in DAGs that run multiple agents simultaneously.
  </Card>

  <Card title="Scheduling and webhooks" icon="calendar" href="/guides/scheduling-webhooks">
    Trigger approval-gated workflows on a schedule or via HTTP webhook.
  </Card>
</CardGroup>
