Skip to main content
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.
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.

Available events

EventWhen it fires
on_dag_startImmediately before the first node is dispatched
on_dag_completeAfter all nodes finish successfully
on_dag_pausedWhen an approval_gate or user_input node pauses the run
on_dag_failedWhen any node raises an unhandled error or budget is exceeded
EventWhen it fires
on_node_startImmediately before a node begins executing
on_node_completeAfter a node finishes successfully
on_node_failedAfter a node fails (after all retry attempts are exhausted)

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.
type
string
required
Must be "webhook".
url
string
required
The POST target URL. Supports ${ENV_VAR} substitution — see Environment variable substitution below.
headers
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.
timeout_seconds
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.
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.
type
string
required
Must be "command".
command
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.
timeout_seconds
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.

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.
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:
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 }}"
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.

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
on_dag_complete:
  - type: command
    command: >
      jq -r '"Run \(.run_id) finished in \(.duration_ms)ms"' <<< "$AGENTGRAPH_EVENT"
      >> ~/dagraph-runs.log