API Endpoints

Full reference for the daggle REST API. Base URL: http://localhost:{port}/api/v1.

System

GET /api/v1/health

Health check.

Response:

{
  "status": "ok",
  "version": "0.5.0",
  "uptime_seconds": 3621
}
curl -s http://localhost:8787/api/v1/health | jq .

DAGs

GET /api/v1/dags

List all DAGs.

Response: flat array.

[
  {
    "name": "etl-daily",
    "steps": 4,
    "schedule": "0 3 * * *",
    "last_status": "success",
    "last_run": "2025-01-15T03:00:12Z"
  }
]
curl -s http://localhost:8787/api/v1/dags | jq .

GET /api/v1/dags/{name}

Get details for a single DAG.

Response:

{
  "name": "etl-daily",
  "steps": 4,
  "step_ids": ["fetch", "transform", "validate", "load"],
  "schedule": "0 3 * * *",
  "workdir": "/home/user/etl",
  "r_version": "4.4.1",
  "last_status": "success",
  "last_run_id": "20250115T030012-abc12",
  "last_run": "2025-01-15T03:00:12Z"
}
curl -s http://localhost:8787/api/v1/dags/etl-daily | jq .

POST /api/v1/dags/{name}/run

Trigger a new run. Returns immediately; poll status via the runs endpoint.

Request body (optional):

{
  "params": {
    "date": "2025-01-15",
    "mode": "full"
  }
}

Response (201):

{
  "run_id": "20250115T103000-abc12",
  "status": "running"
}
curl -s -X POST http://localhost:8787/api/v1/dags/etl-daily/run \
  -H "Content-Type: application/json" \
  -d '{"params": {"date": "2025-01-15"}}' | jq .

POST /api/v1/dags/{name}/validate

Validate a DAG definition without running it.

Response (200):

{
  "valid": true,
  "warnings": []
}
curl -s -X POST http://localhost:8787/api/v1/dags/etl-daily/validate | jq .

Runs

GET /api/v1/dags/{name}/runs

List all runs for a DAG.

Response: flat array.

[
  {
    "run_id": "20250115T030012-abc12",
    "started": "2025-01-15T03:00:12Z",
    "status": "success",
    "duration_seconds": 47,
    "dag_hash": "a1b2c3d4"
  }
]
curl -s http://localhost:8787/api/v1/dags/etl-daily/runs | jq .

GET /api/v1/dags/{name}/runs/{run_id}

Get full detail for a single run, including step statuses.

Use latest as run_id to get the most recent run.

Response:

{
  "run_id": "20250115T030012-abc12",
  "started": "2025-01-15T03:00:12Z",
  "status": "success",
  "duration_seconds": 47,
  "dag_hash": "a1b2c3d4",
  "steps": [
    {"step_id": "fetch", "status": "success", "duration_seconds": 12},
    {"step_id": "transform", "status": "success", "duration_seconds": 20},
    {"step_id": "validate", "status": "success", "duration_seconds": 5},
    {"step_id": "load", "status": "success", "duration_seconds": 10}
  ]
}
curl -s http://localhost:8787/api/v1/dags/etl-daily/runs/latest | jq .

POST /api/v1/dags/{name}/runs/{run_id}/cancel

Cancel a running run.

Response (200):

{
  "run_id": "20250115T103000-abc12",
  "status": "cancelled"
}
curl -s -X POST http://localhost:8787/api/v1/dags/etl-daily/runs/20250115T103000-abc12/cancel | jq .

Steps

GET /api/v1/dags/{name}/runs/{run_id}/steps

List step statuses for a run.

Response: flat array.

[
  {"step_id": "fetch", "status": "success", "duration_seconds": 12},
  {"step_id": "transform", "status": "running", "duration_seconds": null},
  {"step_id": "validate", "status": "pending", "duration_seconds": null}
]
curl -s http://localhost:8787/api/v1/dags/etl-daily/runs/latest/steps | jq .

GET /api/v1/dags/{name}/runs/{run_id}/steps/{step_id}/log

Get stdout and stderr for a step.

Response:

{
  "step_id": "fetch",
  "stdout": "Fetching data from API...\nReceived 1432 rows.\n",
  "stderr": ""
}
curl -s http://localhost:8787/api/v1/dags/etl-daily/runs/latest/steps/fetch/log | jq .

POST /api/v1/dags/{name}/runs/{run_id}/steps/{step_id}/approve

Approve a step that is waiting for approval.

Response (200):

{
  "step_id": "load",
  "status": "approved"
}
curl -s -X POST http://localhost:8787/api/v1/dags/etl-daily/runs/latest/steps/load/approve | jq .

POST /api/v1/dags/{name}/runs/{run_id}/steps/{step_id}/reject

Reject a step that is waiting for approval.

Response (200):

{
  "step_id": "load",
  "status": "rejected"
}
curl -s -X POST http://localhost:8787/api/v1/dags/etl-daily/runs/latest/steps/load/reject | jq .

Outputs

GET /api/v1/dags/{name}/runs/{run_id}/outputs

Get all outputs for a run.

Response: flat array.

[
  {"step_id": "fetch", "key": "row_count", "value": "1432"},
  {"step_id": "transform", "key": "output_path", "value": "/tmp/transformed.parquet"},
  {"step_id": "validate", "key": "passed", "value": "true"}
]
curl -s http://localhost:8787/api/v1/dags/etl-daily/runs/latest/outputs | jq .

Maintenance

POST /api/v1/runs/cleanup

Remove old run data.

Request body:

{
  "older_than": "30d"
}

Response (200):

{
  "removed": 42,
  "freed_bytes": 10485760,
  "freed": "10.0 MB"
}
curl -s -X POST http://localhost:8787/api/v1/runs/cleanup \
  -H "Content-Type: application/json" \
  -d '{"older_than": "30d"}' | jq .

Error responses

All errors return a JSON object with an error field:

{
  "error": "DAG 'foo' not found"
}

Status codes:

Code Meaning
200 Success
201 Created (new run triggered)
400 Bad request (invalid params, malformed body)
404 Resource not found
409 Conflict (e.g., cancelling an already-finished run)
500 Internal server error