Dagu provides a comprehensive REST API for programmatic control over command workflows. The API enables DAG management, execution control, monitoring, and system operations through standard HTTP endpoints.
::: tip API Reference For the complete API documentation with all endpoints, see REST API Reference. :::
- Base URL:
http://localhost:8080/api/v1 - Content-Type:
application/json - Required Headers:
Accept: application/json
Mounted deployments can change both the server base path and the API base path. The live OpenAPI document advertises the mounted API path in servers[0].url so generated clients can target /api/v1, /dagu/api/v1, /dagu/rest, or any other configured mount point correctly.
The REST API supports three authentication methods:
- Basic Authentication: Include
Authorization: Basic <base64(username:password)>header - Bearer Token: Include
Authorization: Bearer <token>header - No Authentication: When auth is disabled (default for local development)
Check server health and status:
curl http://localhost:8080/api/v1/healthResponse includes server status, version, uptime, and timestamp.
Fetch the normalized OpenAPI document served by the current Dagu instance:
curl http://localhost:8080/api/v1/openapi.jsonWhen authentication is enabled, /openapi.json requires the same auth as the rest of the API. The web UI also exposes a live viewer at /api-docs, which loads this document with the current session.
# Get all DAGs
curl http://localhost:8080/api/v1/dags
# With filtering and pagination
curl "http://localhost:8080/api/v1/dags?page=1&perPage=10&name=example&labels=prod"
# Sort alphabetically
curl "http://localhost:8080/api/v1/dags?sort=name&order=desc"
# Sort by the scheduler's next planned run time
curl "http://localhost:8080/api/v1/dags?sort=nextRun&order=asc"::: info Supported Sort Fields
GET /api/v1/dags supports server-side sorting by name and nextRun.
name: case-insensitive DAG name sortnextRun: earlier scheduled run times first in ascending order; DAGs without schedules appear last :::
curl http://localhost:8080/api/v1/dags/my-dagThe DAG detail response includes paramSchema when Dagu can safely render a schema-backed parameter form directly, and paramDefs for typed scalar metadata. paramDefs carries metadata for inline rich params: definitions, top-level inline JSON Schema, and representable external schemas. For named params, clients should submit a JSON object payload; JSON arrays are mainly for positional or mixed raw input.
The same response also exposes histRetentionDays for DAG history retention.
defaultParams is a shell-style string of resolved default pairs such as environment="staging" batch_size="25". It is not JSON.
When a param uses inline eval, paramDefs.default still represents only the authored literal default, if one exists. Computed defaults such as ${BASE_DIR}/out or `nproc` are resolved by the server when the DAG is started.
# Create with default template
curl -X POST http://localhost:8080/api/v1/dags \
-H "Content-Type: application/json" \
-d '{"name": "my-new-dag"}'
# Create with custom specification (validated before creation)
curl -X POST http://localhost:8080/api/v1/dags \
-H "Content-Type: application/json" \
-d '{
"name": "my-new-dag",
"spec": "steps:\n - command: echo hello"
}'curl -X POST http://localhost:8080/api/v1/dags/validate \
-H "Content-Type: application/json" \
-d '{
"spec": "steps:\n - command: echo hello",
"name": "optional-dag-name"
}'Validates DAG specifications without saving. Returns a validity flag, validation errors, and parsed DAG details when possible.
Example response (valid):
{
"valid": true,
"errors": [],
"dag": {
"name": "my-new-dag"
}
}Example response (invalid):
{
"valid": false,
"errors": [
"field 'steps': decoding failed due to the following error(s):",
"invalid keys: command1"
]
}curl -X POST http://localhost:8080/api/v1/dags/my-dag/start \
-H "Content-Type: application/json" \
-d '{
"params": "{\"env\": \"production\"}",
"dagRunId": "custom-run-id",
"dagName": "adhoc-my-dag"
}'params remains a JSON string payload. The server validates and coerces its values against the DAG's inline param definitions or external parameter schema before execution.
curl -X POST http://localhost:8080/api/v1/dags/my-dag/enqueue \
-H "Content-Type: application/json" \
-d '{
"params": "{\"env\": \"production\"}",
"dagName": "queue-override-my-dag",
"queue": "high-priority" // optional: override queue
}'The same validation rules apply to enqueue requests. Invalid typed values are rejected before the run is queued.
# Suspend
curl -X POST http://localhost:8080/api/v1/dags/my-dag/suspend \
-H "Content-Type: application/json" \
-d '{"suspend": true}'
# Resume
curl -X POST http://localhost:8080/api/v1/dags/my-dag/suspend \
-H "Content-Type: application/json" \
-d '{"suspend": false}'# Get queue summaries
curl http://localhost:8080/api/v1/queuesGET /queues returns queue summaries only: queue type (global or dag-based), optional maxConcurrency, runningCount, queuedCount, currently running items, and a top-level capacity summary.
To inspect queue contents, use GET /queues/{name}/items:
# Get queued items from one queue
curl "http://localhost:8080/api/v1/queues/default/items?type=queued&page=1&perPage=20"
# Get running items from one queue
curl "http://localhost:8080/api/v1/queues/default/items?type=running&page=1&perPage=20"# Get the newest DAG runs
curl http://localhost:8080/api/v1/dag-runs
# Filter by name and status
curl "http://localhost:8080/api/v1/dag-runs?name=my-dag&status=2"GET /dag-runs and GET /dag-runs/{name} now use forward-only cursor pagination. Each response returns dagRuns and, when more data exists, nextCursor.
# Follow the next page
curl "http://localhost:8080/api/v1/dag-runs?limit=100&cursor=<opaque-cursor>"Create and start a DAG-run directly from a YAML spec without persisting a DAG file.
curl -X POST "http://localhost:8080/api/v1/dag-runs?remoteNode=local" \
-H "Content-Type: application/json" \
-d '{
"spec": "steps:\n - name: hello\n command: echo hello",
"name": "hello", # optional if spec includes name
"params": "{\"env\":\"prod\"}", # optional JSON string
"dagRunId": "custom-run-id", # optional; autogenerated if omitted
"singleton": true # optional; prevent start if already active
}'Notes:
namemust be <= 40 chars and match[A-Za-z0-9_.-]+when provided.- When
singletonis true, starting while another run of the same DAG name is active returns 409 (max_run_reached). - Response:
{ "dagRunId": "<run-id>" }curl -X POST http://localhost:8080/api/v1/dag-runs/my-dag/20240101_120000/stopThis endpoint stops a running DAG run. It can also cancel a failed root DAG run that is still pending DAG-level automatic retry.
curl -X POST http://localhost:8080/api/v1/dag-runs/my-dag/20240101_120000/retry \
-H "Content-Type: application/json" \
-d '{"dagRunId": "new-run-id"}'# Mark step as successful
curl -X PATCH http://localhost:8080/api/v1/dag-runs/my-dag/20240101_120000/steps/step1/status \
-H "Content-Type: application/json" \
-d '{"status": 4}' # 4 = Success
# Mark step as failed
curl -X PATCH http://localhost:8080/api/v1/dag-runs/my-dag/20240101_120000/steps/step1/status \
-H "Content-Type: application/json" \
-d '{"status": 2}' # 2 = FailedLegacy full DAG search is still available:
curl "http://localhost:8080/api/v1/dags/search?q=database"The global search page uses cursor-based feed endpoints:
# Lightweight DAG search results
curl "http://localhost:8080/api/v1/search/dags?q=database"
# Lightweight document search results
curl "http://localhost:8080/api/v1/search/docs?q=runbook"
# Load more snippets for one DAG result
curl "http://localhost:8080/api/v1/search/dags/example/matches?q=database&cursor=<opaque-cursor>"
# Load more snippets for one document result
curl "http://localhost:8080/api/v1/search/docs/matches?path=runbooks/oncall&q=runbook&cursor=<opaque-cursor>"The feed endpoints return lightweight results with preview matches, hasMore, and optional cursors for both additional results and additional snippets per result.
Centralized event logs are available through GET /event-logs when the event store is enabled:
curl "http://localhost:8080/api/v1/event-logs?kind=dag_run&type=dag.run.failed&limit=50"Supported filters include kind, type, dagName, dagRunId, attemptId, sessionId, userId, model, startTime, and endTime.
The endpoint supports:
- offset pagination with
limitandoffset - cursor pagination with
limit,paginationMode=cursor, andcursor
When builtin authentication is enabled, this endpoint requires a manager or admin role.
Dagu exposes Prometheus-compatible metrics for comprehensive monitoring:
curl http://localhost:8080/api/v1/metricsAvailable metrics include:
- System Metrics: Build info, uptime, scheduler status
- Execution Metrics: Running DAGs, queued runs, execution totals by status
- Worker Metrics: Registered workers, worker labels, worker health, poller capacity, running tasks
- Performance Metrics: Success rates, queue lengths
All API endpoints return structured error responses:
{
"code": "error_code",
"message": "Human readable error message",
"details": {
"additional": "error details"
}
}Common error codes:
bad_request: Invalid request parametersnot_found: Resource doesn't existinternal_error: Server-side errorunauthorized: Authentication failedforbidden: Insufficient permissionsalready_exists: Resource (e.g., run ID) already existsmax_run_reached: Concurrency or singleton limit reachednot_running: DAG is not running
{
"dags": [
{
"fileName": "example",
"dag": {
"name": "example_dag",
"schedule": [{"expression": "0 * * * *"}],
"description": "Example DAG",
"labels": ["example", "demo"]
},
"nextRun": "2026-03-29T09:30:00+09:00",
"latestDAGRun": {
"dagRunId": "20240101_120000",
"name": "example_dag",
"status": 1,
"statusLabel": "running",
"startedAt": "2024-01-01T12:00:00Z"
},
"suspended": false
}
],
"errors": [],
"pagination": {
"totalRecords": 45,
"currentPage": 1,
"totalPages": 5,
"nextPage": 2,
"prevPage": null
}
}schedule entries are recurring cron objects such as {"expression":"0 * * * *"} or typed one-off start entries such as {"kind":"at","at":"2026-03-29T09:30:00+09:00"}. nextRun is the scheduler-aware next planned run time for that DAG. For a pending one-off start entry, nextRun can remain visible even after that timestamp has passed, until the scheduler consumes it.
{
"status": "healthy",
"version": "1.0.0",
"uptime": 3600,
"timestamp": "2024-02-11T12:00:00Z"
}