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

# Trigger dagraph workflows via webhook or cron schedule

> Run dagraph workflows automatically on a cron schedule or in response to HTTP POST requests using the serve and schedule commands.

`agentgraph schedule` and `agentgraph serve` turn a DAG YAML into a long-running service. `schedule` fires the workflow on a cron expression and blocks until you press Ctrl+C. `serve` starts an HTTP server that runs the workflow when it receives a POST request, and optionally also runs on a cron schedule. Both commands require the `serve` extras package.

```bash theme={null}
pip install 'dagraph[serve]'
```

<Tabs>
  <Tab title="schedule">
    ## agentgraph schedule

    Run a DAG on a repeating cron schedule. Each tick creates a new run with a fresh run ID. If the previous run is still in progress when the next tick fires, that tick is skipped and a warning is logged — runs never overlap.

    ```
    agentgraph schedule <dag_path> --cron "<expression>" [OPTIONS]
    ```

    ### Arguments and flags

    <ParamField path="dag_path" type="string" required>
      Path to the DAG YAML file.
    </ParamField>

    <ParamField path="--cron" type="string" required>
      5-field cron expression in the format `minute hour day month weekday`. For example, `0 9 * * 1-5` runs at 09:00 on weekdays.
    </ParamField>

    <ParamField path="--input / -i" type="string">
      Static inputs passed to every run. Repeat for multiple values. These are the same across all ticks; use a `user_input` node if you need dynamic values per run.
    </ParamField>

    <ParamField path="--runs-dir" type="string" default="runs">
      Directory where run subdirectories are created.
    </ParamField>

    <ParamField path="--backend" type="string" default="claude_code">
      Executor backend. See [`agentgraph run`](/cli/run) for options.
    </ParamField>

    <ParamField path="--sandbox" type="string">
      Sandbox for exec nodes (`inprocess` or `docker`).
    </ParamField>

    <ParamField path="--rpm" type="number">
      Requests-per-minute cap across all nodes.
    </ParamField>

    <ParamField path="--max-concurrent" type="number" default="10">
      Maximum simultaneous LLM calls per run.
    </ParamField>

    ### Examples

    <CodeGroup>
      ```bash Run every weekday at 09:00 theme={null}
      agentgraph schedule examples/research.yaml \
        --cron "0 9 * * 1-5" \
        --input topic="AI news"
      ```

      ```bash Run every 6 hours theme={null}
      agentgraph schedule examples/digest.yaml \
        --cron "0 */6 * * *" \
        --input audience="engineering team"
      ```

      ```bash Run every 30 minutes with rate limiting theme={null}
      agentgraph schedule examples/monitor.yaml \
        --cron "*/30 * * * *" \
        --backend api \
        --rpm 20
      ```
    </CodeGroup>

    ### Non-overlapping execution

    If a run is still in progress when the next cron tick fires, the scheduler skips that tick and logs a warning:

    ```
    ⚠ cron tick skipped: previous run still in progress (job ...)
    ```

    This guarantees at most one concurrent execution of your DAG, regardless of how long each run takes.
  </Tab>

  <Tab title="serve">
    ## agentgraph serve

    Start an HTTP server that triggers a DAG run when it receives a POST request. The JSON body keys are merged with any `--input` defaults and passed to the DAG as template inputs. The server responds synchronously with the run ID and status once the run completes.

    You can combine a webhook trigger and a cron schedule in a single `serve` invocation.

    ```
    agentgraph serve <dag_path> [OPTIONS]
    ```

    ### Arguments and flags

    <ParamField path="dag_path" type="string" required>
      Path to the DAG YAML file.
    </ParamField>

    <ParamField path="--webhook" type="string">
      URL path to expose for POST requests, for example `/trigger`. At least one of `--webhook` or `--cron` must be provided.
    </ParamField>

    <ParamField path="--cron" type="string">
      Also run the DAG on a cron schedule while the server is running.
    </ParamField>

    <ParamField path="--webhook-secret" type="string">
      If set, all incoming requests must include the header `Authorization: Bearer <secret>`. Reads from the `AGENTGRAPH_WEBHOOK_SECRET` environment variable if the flag is not passed explicitly.
    </ParamField>

    <ParamField path="--host" type="string" default="127.0.0.1">
      Host address to bind the server to.
    </ParamField>

    <ParamField path="--port" type="number" default="8000">
      Port to listen on.
    </ParamField>

    <ParamField path="--input / -i" type="string">
      Base inputs merged with the webhook JSON body on every request. Webhook body values take precedence over `--input` defaults.
    </ParamField>

    <ParamField path="--runs-dir" type="string" default="runs">
      Directory where run subdirectories are created.
    </ParamField>

    <ParamField path="--backend" type="string" default="claude_code">
      Executor backend.
    </ParamField>

    <ParamField path="--sandbox" type="string">
      Sandbox for exec nodes.
    </ParamField>

    <ParamField path="--rpm" type="number">
      Requests-per-minute cap.
    </ParamField>

    <ParamField path="--max-concurrent" type="number" default="10">
      Maximum simultaneous LLM calls.
    </ParamField>

    ### Examples

    <CodeGroup>
      ```bash Basic webhook server theme={null}
      agentgraph serve examples/research.yaml --webhook /trigger
      ```

      ```bash Webhook with authentication theme={null}
      agentgraph serve examples/research.yaml \
        --webhook /trigger \
        --webhook-secret my-secret-token
      ```

      ```bash Webhook plus cron schedule theme={null}
      agentgraph serve examples/digest.yaml \
        --webhook /trigger \
        --cron "0 8 * * 1-5" \
        --input audience="team"
      ```

      ```bash Public-facing server theme={null}
      agentgraph serve examples/research.yaml \
        --webhook /trigger \
        --host 0.0.0.0 \
        --port 9000 \
        --backend api
      ```
    </CodeGroup>

    ### Triggering the webhook

    Send a POST request with a JSON body. Body keys are merged with any `--input` defaults and passed to the DAG as template variables:

    ```bash theme={null}
    curl -X POST http://localhost:8000/trigger \
      -H "Content-Type: application/json" \
      -d '{"topic": "quantum computing"}'
    ```

    With authentication:

    ```bash theme={null}
    curl -X POST http://localhost:8000/trigger \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer my-secret-token" \
      -d '{"topic": "quantum computing"}'
    ```

    ### Webhook response

    The server responds synchronously after the run completes:

    ```json theme={null}
    {
      "run_id": "a1b2c3d4e5f6",
      "status": "completed"
    }
    ```

    Possible status values are `completed`, `failed`, and `paused` (if the DAG hit an approval gate).

    <Warning>
      `serve` runs each triggered DAG synchronously in the same process. For high-throughput use cases, consider the dagraph cloud or self-hosted control-plane, which queues runs in a job worker.
    </Warning>
  </Tab>
</Tabs>
