Skip to content

Worker API

All worker endpoints require authentication via the Authorization header:

Authorization: Bearer <worker_token>

The token is configured in server-config.yaml (worker_token field) and must match the worker_token in worker-config.yaml.

POST /worker/register

Registers a worker and returns a unique worker ID. Called once on worker startup.

Request body:

{
"name": "worker-1",
"tags": ["script", "docker"]
}
FieldTypeDescription
namestringWorker display name
tagsstring[]Worker tags for step routing

Response:

{
"worker_id": "w1w2w3w4-w5w6-7890-abcd-ef1234567890"
}
POST /worker/heartbeat

Updates the worker’s last-seen timestamp. Called periodically (every 30s). Also reactivates workers that were marked inactive.

Request body:

{
"worker_id": "w1w2w3w4-w5w6-7890-abcd-ef1234567890"
}
POST /worker/jobs/claim

Claims the next ready step that matches the worker’s tags. Uses SELECT FOR UPDATE SKIP LOCKED for concurrency safety.

Request body:

{
"worker_id": "w1w2w3w4-...",
"tags": ["script", "docker"]
}

A step is only claimed if all of its required_tags are present in the worker’s tag set.

Response (step available):

{
"job_id": "a1b2c3d4-...",
"workspace": "default",
"step_name": "say-hello",
"action_name": "greet",
"action_type": "script",
"action_image": null,
"runner": "local",
"action_spec": {
"script": "echo Hello World",
"env": {}
},
"input": { "name": "World" }
}

The action_spec contains the fully resolved action definition with templates already rendered. The runner field indicates how to execute: local, docker, pod, or none (for type: docker/type: pod actions).

Response (no work):

{
"job_id": null,
"step_name": null,
"action_spec": null
}
POST /worker/jobs/{id}/steps/{step}/start

Marks a step as actively running.

Request body:

{
"worker_id": "w1w2w3w4-..."
}
POST /worker/jobs/{id}/steps/{step}/complete

Reports step completion or failure. Triggers the orchestrator to promote dependent steps.

Request body (success):

{
"output": { "greeting": "Hello World" },
"exit_code": 0,
"error": null
}

Request body (failure):

{
"output": null,
"exit_code": 1,
"error": "Command exited with code 1"
}

When a step completes, the orchestrator checks downstream dependencies. When a step fails, dependent steps are skipped (unless they have continue_on_failure: true).

POST /worker/jobs/{id}/logs

Appends structured log lines to the job’s JSONL log file. Called periodically (~1s) during step execution.

Request body:

{
"step_name": "say-hello",
"lines": [
{"ts": "2025-02-12T10:56:45.123Z", "stream": "stdout", "line": "Hello World"},
{"ts": "2025-02-12T10:56:45.456Z", "stream": "stderr", "line": "warning: unused var"}
]
}

The server appends each line as a JSONL entry and broadcasts via WebSocket for live streaming.

GET /worker/workspace/{ws}.tar.gz

Downloads a workspace as a gzipped tar archive.

ParameterDescription
wsWorkspace name

Headers:

  • If-None-Match — Revision ETag for conditional fetch

Response headers:

  • Content-Type: application/gzip
  • X-Revision: {revision}
  • ETag: "{revision}"
StatusDescription
200Tarball returned
304Not Modified (workspace unchanged)
404Workspace not found
POST /worker/jobs/{id}/complete

Marks an entire job as completed. Used in local execution mode where the worker handles the full DAG.

Request body:

{
"output": { "result": "success" }
}