> ## 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.

# Configure lifecycle hooks for dagraph workflow events

> Fire webhook POSTs or shell commands when a DAG starts, completes, pauses, or fails — and when individual nodes start, complete, or fail.

Lifecycle hooks let you integrate dagraph runs into your existing notification and monitoring infrastructure without modifying the DAG logic itself. You attach hooks to named events in the `hooks:` field of your YAML, and dagraph fires them asynchronously at the right moment. A Slack notification when a run completes, a pager alert when it fails, a log entry when a node starts — all of these are one YAML stanza away.

## How hooks work

Hooks are fire-and-forget. They run concurrently with the normal DAG execution flow but never block it. If a hook fails — network error, non-zero exit code, timeout — dagraph logs a warning and moves on. A broken hook endpoint can never fail your DAG.

<Note>
  Hook failures are always swallowed. If you need reliable delivery of run events, use a webhook endpoint with its own retry logic, or write to a durable queue in a command hook.
</Note>

## Available events

<AccordionGroup>
  <Accordion title="DAG-level events">
    | Event             | When it fires                                                 |
    | ----------------- | ------------------------------------------------------------- |
    | `on_dag_start`    | Immediately before the first node is dispatched               |
    | `on_dag_complete` | After all nodes finish successfully                           |
    | `on_dag_paused`   | When an `approval_gate` or `user_input` node pauses the run   |
    | `on_dag_failed`   | When any node raises an unhandled error or budget is exceeded |
  </Accordion>

  <Accordion title="Node-level events">
    | Event              | When it fires                                               |
    | ------------------ | ----------------------------------------------------------- |
    | `on_node_start`    | Immediately before a node begins executing                  |
    | `on_node_complete` | After a node finishes successfully                          |
    | `on_node_failed`   | After a node fails (after all retry attempts are exhausted) |
  </Accordion>
</AccordionGroup>

## Hook types

### Webhook hook

The `webhook` type sends an HTTP POST with a JSON body to the URL you specify. Use it for Slack incoming webhooks, PagerDuty events, custom API endpoints, or any HTTP receiver.

<ParamField path="type" type="string" required>
  Must be `"webhook"`.
</ParamField>

<ParamField path="url" type="string" required>
  The POST target URL. Supports `${ENV_VAR}` substitution — see [Environment variable substitution](#environment-variable-substitution) below.
</ParamField>

<ParamField path="headers" type="object">
  A map of header name to value. Values support `${ENV_VAR}` substitution. Use this for `Authorization` headers, `Content-Type` overrides, or any custom headers your endpoint requires.
</ParamField>

<ParamField path="timeout_seconds" type="number" default="5.0">
  How long to wait for the HTTP response before giving up. Maximum 60 seconds. Keep this low — a slow webhook should not hold up run completion.
</ParamField>

The POST body is a JSON object containing `"event"` (the event name) plus all available context fields for that event, such as `run_id`, `dag_name`, `node_id`, `status`, and timing information.

### Command hook

The `command` type runs a shell command. Use it when you need more flexibility than a simple HTTP call — for example, piping run data into a script, appending to a log file, or integrating with tools that have a CLI.

<ParamField path="type" type="string" required>
  Must be `"command"`.
</ParamField>

<ParamField path="command" type="string" required>
  The shell command to run. Shell expansion is active, so you can use pipes, redirects, and variable substitution via normal shell syntax. The event payload is available two ways: as JSON on stdin, and in the `AGENTGRAPH_EVENT` environment variable.
</ParamField>

<ParamField path="timeout_seconds" type="number" default="10.0">
  How long to wait for the command to exit. Maximum 120 seconds. Stdout and stderr are captured so a verbose command does not pollute the run console.
</ParamField>

## Environment variable substitution

Webhook URLs and header values support `${VAR}` syntax for environment variable substitution. This lets you keep secrets out of your YAML files — reference them by environment variable name instead of embedding them directly.

```yaml theme={null}
hooks:
  on_dag_complete:
    - type: webhook
      url: https://hooks.slack.com/services/${SLACK_TOKEN}
      headers:
        Authorization: "Bearer ${NOTIFY_API_KEY}"
        Content-Type: application/json
```

If an environment variable is not set at runtime, dagraph logs a warning and substitutes an empty string. Command hooks do not use `${VAR}` substitution — use normal shell variable expansion (`$VAR`) in the command string instead, since the OS shell handles that.

## Complete example

The following example sends a Slack notification on success, triggers a PagerDuty alert on failure, and logs node completions to a local file:

```yaml theme={null}
name: research
description: Parallel research with full lifecycle hooks.

budget:
  max_tokens: 50000
  max_usd: 2.00

hooks:
  on_dag_start:
    - type: webhook
      url: https://hooks.slack.com/services/${SLACK_TOKEN}
      headers:
        Content-Type: application/json
      timeout_seconds: 5.0

  on_dag_complete:
    - type: webhook
      url: https://hooks.slack.com/services/${SLACK_TOKEN}
      headers:
        Content-Type: application/json

  on_dag_failed:
    - type: webhook
      url: https://events.pagerduty.com/v2/enqueue
      headers:
        Authorization: "Token token=${PAGERDUTY_KEY}"
        Content-Type: application/json
      timeout_seconds: 10.0
    - type: command
      command: "echo 'DAG failed' | mail -s 'dagraph alert' ops@example.com"

  on_node_complete:
    - type: command
      command: "echo \"$AGENTGRAPH_EVENT\" >> /var/log/dagraph/nodes.jsonl"
      timeout_seconds: 2.0

nodes:
  - id: researcher
    type: agent
    model: claude-haiku-4-5-20251001
    prompt: "Research {{ topic }} and return 5-8 bullet points."

  - id: synthesizer
    type: agent
    model: claude-sonnet-4-6
    depends_on: [researcher]
    prompt: "Write a report based on: {{ researcher }}"
```

<Tip>
  You can attach multiple hooks to the same event — they fire independently and in parallel. A failure in one hook does not prevent the others from firing.
</Tip>

## Accessing event data in command hooks

The full event payload is available inside a command hook as a JSON string in two places:

* **stdin** — pipe it directly into tools that read from stdin: `jq '.run_id' <<< "$AGENTGRAPH_EVENT"`
* **`AGENTGRAPH_EVENT` environment variable** — reference it in the command string

```yaml theme={null}
on_dag_complete:
  - type: command
    command: >
      jq -r '"Run \(.run_id) finished in \(.duration_ms)ms"' <<< "$AGENTGRAPH_EVENT"
      >> ~/dagraph-runs.log
```
