Last updated 2026-05-07
Runner REST API
Every endpoint exposed by the runner. Defaults to
http://localhost:4800; override the port with
PORT=.... CORS is enabled, so the dashboard and any
other origin can call directly. Routes are mounted under
/api; the only exception is /health.
Workflow runs
POST /api/runs — Trigger a run
{ "workflow": "ci", // required: filename without ext, full filename, or workflow name "inputs": { "skip-tests": "false" }, "sandbox": { "strategy": "worktree", // worktree | copy | none. Default: auto. "ref": "feature-branch" // optional. Default: HEAD. }}Response: 201 Created with the new WorkflowRun.
curl -X POST http://localhost:4800/api/runs \ -H 'Content-Type: application/json' \ -d '{"workflow": "ci", "inputs": {"skip-tests": "false"}}'{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "workflow": "ci", "status": "queued", "jobs": [], "createdAt": "2026-03-22T10:00:00.000Z", "updatedAt": "2026-03-22T10:00:00.000Z", "trigger": { "event": "workflow_dispatch", "ref": "refs/heads/main", "sha": "abc123def456" }, "inputs": { "skip-tests": "false" }}GET /api/runs — List runs
Query: ?status=queued|in_progress|completed|failed|cancelled
— optional filter. Sorted newest first.
curl http://localhost:4800/api/runscurl 'http://localhost:4800/api/runs?status=completed'GET /api/runs/:id — Run detail
Full WorkflowRun with jobs and steps. 404 when not found.
GET /api/runs/:id/logs — Concatenated logs
Plain-text response with one section per job and step.
=== Job: build (build) ===--- Step 1: Checkout [success] ---Workspace already available, no checkout needed--- Step 2: Install dependencies [success] ---npm ci...GET /api/runs/:id/artifacts — List artifacts
[ { "id": "b2c3d4e5-f6a7-8901-bcde-f23456789012", "name": "coverage-report", "path": "/path/to/.artifacts/a1b2c3d4/coverage-report.html", "size": 42567, "mimeType": "text/html", "runId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }]GET /api/runs/:id/artifacts/:name — Download an artifact
Streams the file with Content-Disposition: attachment.
curl -o report.html http://localhost:4800/api/runs/a1b2c3d4/artifacts/coverage-reportPOST /api/runs/:id/cancel — Cancel a run
Sends an abort signal. Works on queued or
in_progress; returns 409 otherwise.
Workflows
GET /api/workflows
Discovers every .yml / .yaml under
.github/workflows/ and returns parsed metadata.
[ { "name": "CI", "fileName": "ci.yml", "filePath": "/path/to/.github/workflows/ci.yml", "triggers": ["push", "pull_request", "workflow_dispatch"], "jobs": [ { "id": "test", "name": "Run Tests", "needs": [] }, { "id": "deploy", "name": "Deploy", "needs": ["test"] } ], "inputs": { "environment": { "description": "Target environment", "required": false, "default": "staging", "type": "choice", "options": ["staging", "production"] } } }]GET /api/workflows/:name
{ "info": { "name": "CI", "fileName": "ci.yml", "filePath": "/path/to/.github/workflows/ci.yml", "triggers": ["push", "workflow_dispatch"], "jobs": [...], "inputs": {...} }, "definition": { /* raw parsed YAML */ }}Secrets
GET /api/secrets
Returns secret names only for the requested environment
(?env=production). Values are never exposed.
["GITHUB_TOKEN", "NPM_TOKEN", "AWS_ACCESS_KEY_ID"]PUT /api/secrets/:name
curl -X PUT http://localhost:4800/api/secrets/NPM_TOKEN \ -H 'Content-Type: application/json' \ -d '{"value": "npm_abc123", "environment": "production", "backend": "local"}'DELETE /api/secrets/:name
curl -X DELETE http://localhost:4800/api/secrets/NPM_TOKENPOST /api/secrets/import
.env-formatted content: KEY=VALUE lines,
# comments, blank lines, optional quoted values. Set
type to variable to import into the variables
store instead of the secrets store.
curl -X POST http://localhost:4800/api/secrets/import \ -H 'Content-Type: application/json' \ -d '{"content": "NPM_TOKEN=abc\n# comment\nAWS_KEY=xyz", "environment": "production"}'POST /api/secrets/sync
Pulls from active backends into the local runner mirror. For repo-backed
SOPS + age, this decrypts the managed file from .stax/vault.json.
GET /api/secrets/status
Returns backend availability and mirror status.
GET /api/variables, PUT /api/variables/:name, DELETE /api/variables/:name
Variables mirror GitHub Actions vars.*. They are environment
scoped like secrets but are not masked.
GET /api/audit/secrets
Returns the local secrets audit log.
GitHub sync & deployments
GET /api/sync/config / PUT /api/sync/config
Reads or writes GitHub sync configuration, webhook HMAC settings, and deployment-log settings.
{ "enabled": true, "github": { "owner": "elloloop", "repo": "stax", "syncIssues": true, "syncDiscussions": false, "syncWiki": false, "syncReleases": false, "pollIntervalMs": 30000, "webhookSecret": "optional" }, "deploymentLog": { "enabled": true, "issueNumber": 42, "environments": ["production", "prod"] }}POST /api/sync/github
Runs a pull sync for enabled GitHub issues, discussions, wiki pages, and releases.
POST /api/sync/pr
Pushes a local stacked diff to GitHub as a PR branch.
POST /api/sync/inbox
Receives signed GitHub webhook events. Uses
STAX_GH_WEBHOOK_SECRET or the configured webhook secret to
verify x-hub-signature-256.
GET /api/sync/issues, /discussions, /wiki, /releases, /audit
Returns the local GitHub sync caches and sync audit log.
GET /api/deployments
Returns local deployment ledger records from
.stax/deployments.jsonl, newest first. Records include run ID,
workflow, status, environment, ref, SHA, job summary, and optional GitHub
issue mirror metadata.
Events & health
GET /api/events
Server-Sent Events stream. See SSE Events.
GET /health
{ "status": "ok", "workspace": "/path/to/your/project" }Type definitions
type RunStatus = 'queued' | 'in_progress' | 'completed' | 'failed' | 'cancelled';type Conclusion = 'success' | 'failure' | 'cancelled' | 'skipped';
interface WorkflowRun { id: string; workflow: string; status: RunStatus; conclusion?: Conclusion; jobs: Job[]; createdAt: string; // ISO 8601 updatedAt: string; // ISO 8601 trigger: { event: string; ref: string; sha: string }; inputs?: Record<string, string>; env?: Record<string, string>; sandbox?: { path: string; strategy: string };}
interface Job { id: string; name: string; status: 'queued' | 'in_progress' | 'completed'; conclusion?: Conclusion; steps: Step[]; startedAt?: string; completedAt?: string; outputs: Record<string, string>;}
interface Step { name: string; status: 'queued' | 'in_progress' | 'completed'; conclusion?: Conclusion; log: string; outputs: Record<string, string>; number: number;}
interface Artifact { id: string; name: string; path: string; size: number; mimeType: string; runId: string;}
interface WorkflowInfo { name: string; fileName: string; filePath: string; triggers: string[]; jobs: { id: string; name: string; needs: string[] }[]; inputs?: Record<string, WorkflowInput>;}
interface WorkflowInput { description?: string; required?: boolean; default?: string; type?: string; options?: string[];}