Version: v3.3
Last Updated: 2026-02-15
Base URL: http://localhost:3001/api
All API endpoints currently accept an optional authentication key via x-api-key header. RBAC permissions are enforced based on workflow ACLs (Access Control Lists).
Header:
x-api-key: <your-api-key>
Permissions:
view — View workflow definitions and runscreate — Create new workflowsedit — Update workflow definitionsdelete — Delete workflows (owner only)execute — Start workflow runs, approve gatesList all workflows (metadata only, filtered by user permissions).
Request:
curl http://localhost:3001/api/workflows
Response:
[
{
"id": "feature-dev",
"name": "Feature Development Workflow",
"version": 2,
"description": "End-to-end feature development pipeline"
},
{
"id": "security-audit",
"name": "Security Audit & Remediation",
"version": 1,
"description": "Scan, prioritize, and fix security issues"
}
]
Status Codes:
200 OK — SuccessPermissions: Any authenticated user can list workflows they have view permission for.
Get a specific workflow definition (full YAML content).
Request:
curl http://localhost:3001/api/workflows/feature-dev
Response:
{
"id": "feature-dev",
"name": "Feature Development Workflow",
"version": 2,
"description": "End-to-end feature development pipeline",
"config": {
"timeout": 7200,
"fresh_session_default": true,
"progress_file": "progress.md",
"telemetry_tags": ["workflow", "feature-dev"]
},
"agents": [
{
"id": "planner",
"name": "Planner",
"role": "planner",
"model": "github-copilot/claude-opus-4.6",
"description": "Decomposes tasks into user stories"
},
{
"id": "developer",
"name": "Developer",
"role": "developer",
"model": "github-copilot/claude-sonnet-4.5",
"description": "Implements features"
}
],
"steps": [
{
"id": "plan",
"name": "Plan: Decompose into stories",
"type": "agent",
"agent": "planner",
"input": "Decompose this task into stories...",
"output": {
"file": "plan.yml"
},
"acceptance_criteria": ["stories:"],
"on_fail": {
"retry": 2,
"escalate_to": "human"
},
"timeout": 600
}
],
"variables": {
"repo_path": "",
"test_command": "npm test"
}
}
Status Codes:
200 OK — Success404 Not Found — Workflow not found403 Forbidden — No view permissionHeaders:
ETag: "workflow:feature-dev:2"
X-Resource-Revision: 2
Permissions: Requires view permission.
Resolve workflow-specific actions and provenance for the current identity. Clients use this server-owned result to distinguish editable user workflows from built-in or shared read-only definitions.
Request:
curl http://localhost:3001/api/workflows/feature-dev/access
Response:
{
"workflowId": "feature-dev",
"canView": true,
"canEdit": false,
"canExecute": true,
"canDuplicate": true,
"readOnlyReason": "Built-in workflows are read-only. Duplicate this workflow to customize it.",
"provenance": {
"kind": "built-in",
"owner": "system",
"createdBy": "system",
"updatedBy": "system",
"createdAt": "2026-02-09T12:00:00Z",
"updatedAt": "2026-02-09T14:30:00Z"
}
}
provenance.kind is built-in, user-owned, or shared. canEdit and canExecute combine the authenticated request permissions with the workflow ACL decision. canDuplicate reflects whether the authenticated request may create workflows. A read-only response includes an actionable reason suitable for the workflow browser.
Status Codes:
200 OK — Access and provenance resolved403 Forbidden — No view permission404 Not Found — Workflow not foundPermissions: Requires workflow:read and workflow-level view permission.
Create a new workflow.
Request:
curl -X POST http://localhost:3001/api/workflows \
-H "Content-Type: application/json" \
-d '{
"id": "hello-world",
"name": "Hello World Workflow",
"version": 1,
"description": "A simple test workflow",
"agents": [
{
"id": "writer",
"name": "Writer",
"role": "developer",
"model": "github-copilot/claude-sonnet-4.5",
"description": "Writes messages"
}
],
"steps": [
{
"id": "greet",
"name": "Greet user",
"type": "agent",
"agent": "writer",
"input": "Write a hello message",
"output": {
"file": "greeting.md"
}
}
]
}'
Response:
{
"success": true,
"workflowId": "hello-world"
}
Status Codes:
201 Created — Workflow created successfully400 Bad Request — Validation error (missing required fields, invalid references)409 Conflict — Workflow ID already existsPermissions: Any authenticated user can create workflows (becomes owner).
Validation:
id: Required, alphanumeric + dashes, max 100 charactersname: Required, max 200 charactersversion: Required, integer ≥ 0description: Required, max 2000 charactersagents: Required, 1-20 agentssteps: Required, 1-50 stepsstep.agent references must match an agents.idon_fail.retry_step references must match a steps.idUpdate an existing workflow (auto-increments version).
Request:
curl -X PUT http://localhost:3001/api/workflows/hello-world \
-H "Content-Type: application/json" \
-H 'If-Match: "workflow:hello-world:1"' \
-d '{
"id": "hello-world",
"name": "Hello World Workflow v2",
"version": 1,
"description": "Updated workflow with farewell step",
"agents": [
{
"id": "writer",
"name": "Writer",
"role": "developer",
"model": "github-copilot/claude-sonnet-4.5",
"description": "Writes messages"
}
],
"steps": [
{
"id": "greet",
"name": "Greet user",
"type": "agent",
"agent": "writer",
"input": "Write a hello message",
"output": {
"file": "greeting.md"
}
},
{
"id": "farewell",
"name": "Say goodbye",
"type": "agent",
"agent": "writer",
"input": "Write a goodbye message",
"output": {
"file": "farewell.md"
}
}
]
}'
Response:
{
"success": true,
"version": 2
}
Status Codes:
200 OK — Workflow updated successfully400 Bad Request — Validation error or ID mismatch404 Not Found — Workflow not found403 Forbidden — No edit permission409 Conflict — Workflow has changed since the supplied If-Match revisionPermissions: Requires edit permission.
Notes:
version in request body)version is also the optimistic-concurrency revision. Read the
workflow ETag, then send it back with If-Match on update.createdBy, updatedBy, createdAt, and
updatedAt when saved through the API..veritas-kanban/workflows/.audit.jsonlConflict response:
{
"code": "CONFLICT",
"message": "workflow hello-world has changed since it was loaded. Reload and retry with the latest revision.",
"details": {
"resourceType": "workflow",
"resourceId": "hello-world",
"expectedRevision": 1,
"currentRevision": 2,
"current": {
"id": "hello-world",
"version": 2,
"description": "Latest workflow body"
}
}
}
Delete a workflow.
Request:
curl -X DELETE http://localhost:3001/api/workflows/hello-world
Response:
(Empty body, 204 status)
Status Codes:
204 No Content — Workflow deleted successfully404 Not Found — Workflow not found403 Forbidden — Not owner (only owners can delete)Permissions: Requires delete permission (owner only).
Notes:
Validate an unsaved workflow without starting a run. The response includes lint messages, executable checks, and a skill audit for referenced shared skills.
Request:
curl -X POST http://localhost:3001/api/workflows/authoring/dry-run \
-H "Content-Type: application/json" \
-d '{
"workflow": {
"id": "release-helper",
"name": "Release Helper",
"version": 1,
"description": "Uses a shared release skill",
"agents": [
{
"id": "runner",
"name": "Runner",
"role": "developer",
"tools": ["Read", "skill:release-helper"]
}
],
"steps": [{ "id": "run", "name": "Run", "type": "agent", "agent": "runner" }]
},
"context": { "clientMode": "remote" }
}'
Response excerpt:
{
"status": "blocked",
"canRun": false,
"checks": [{ "id": "skill", "label": "Skill audit", "status": "fail" }],
"messages": [
{
"category": "skill",
"severity": "error",
"message": "Skill Release Helper has no persisted scan and cannot run in remote mode."
}
],
"skillAudit": {
"status": "fail",
"mode": "remote",
"references": [
{
"reference": "release-helper",
"skillId": "release-helper",
"status": "blocked",
"message": "Skill Release Helper has no persisted scan and cannot run in remote mode."
}
]
}
}
The skill audit recognizes skill:<id> and skill/<id> references in agents,
tools, steps, variables, inputs, and descriptions. Local mode warns on unscanned
skills. Remote and cloud modes fail missing, unscanned, or blocked skills unless
the skill has an active reviewed exception.
Dry-run responses may include pipelineSummary when a workflow declares
pipeline. Pipeline lint validates the parent agent, subagent role references,
deliverable contracts, verification steps, dependencies, and whether each role is
used by a workflow step or parallel substep.
Returns reusable workflow recipes. v5 recipes include .openclaw Audit, which
materializes an orchestrated pipeline with config, storage, security, docs, and
follow-up task subagent roles.
Materializes a recipe into workflow JSON/YAML and a preview. When a recipe has a
pipeline, preview.pipeline contains role, status, scope, deliverable,
dependency, verification, and telemetry-budget metadata for the authoring UI.
Dry-run a saved workflow definition with the same response shape and context
rules as /api/workflows/authoring/dry-run.
Start a new workflow run.
Request:
curl -X POST http://localhost:3001/api/workflows/feature-dev/runs \
-H "Content-Type: application/json" \
-d '{
"taskId": "US-42",
"context": {
"clientMode": "remote",
"priority": "high",
"deadline": "2026-02-15"
}
}'
Request Body:
{
taskId?: string; // Optional: VK task ID to associate with run
context?: {
clientMode?: "local" | "remote" | "cloud"; // Optional: workflow skill gate mode
[key: string]: unknown;
};
budget?: {
enabled?: boolean;
limits?: {
totalTokens?: number;
costUsd?: number;
toolCalls?: number;
runtimeSeconds?: number;
idleRuntimeSeconds?: number;
retries?: number;
fanOut?: number;
};
softThresholdPercent?: number;
hardAction?: "pause" | "require-approval" | "downgrade" | "cancel";
downgradeModel?: string;
};
}
Before a run starts, the server dry-runs the saved workflow and blocks remote or
cloud execution when a referenced shared skill is missing, unscanned, or blocked.
The run context includes the resulting skillAudit summary when execution is
allowed. Workflows with pipeline metadata also include context.pipeline,
which rolls subagent role status and time/token telemetry into the run record.
Run budgets are merged with workspace, workflow, and workflow-agent defaults
using the strictest positive limit. Soft thresholds write budget-policy
governance traces. Hard thresholds pause/block, require approval, downgrade the
model route, or cancel according to the effective policy.
The workflow root reserves durable admission capacity before the run becomes
active. Every provider-backed step then obtains a child reservation against
its resolved provider and selected host before its attempt becomes running.
The response exposes the root binding and the latest step decision without
including prompts or credentials. Use /api/admission?workflowRunId=<run-id>
or vk admission list --workflow-run <run-id> for current lease and limiting
policy details.
Response:
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"workflowVersion": 2,
"taskId": "US-42",
"status": "running",
"currentStep": "plan",
"context": {
"task": {
"id": "US-42",
"title": "Implement user registration",
"description": "Add registration endpoint with email validation"
},
"priority": "high",
"deadline": "2026-02-15"
},
"startedAt": "2026-02-09T12:00:00Z",
"steps": [
{
"stepId": "plan",
"status": "running",
"agent": "planner",
"startedAt": "2026-02-09T12:00:00Z",
"retries": 0
},
{
"stepId": "implement",
"status": "pending",
"retries": 0
}
]
}
Status Codes:
201 Created — Run started successfully400 Bad Request — Validation error404 Not Found — Workflow not found403 Forbidden — No execute permissionPermissions: Requires execute permission.
Notes:
/api/workflow-runs/:id.veritas-kanban/workflow-runs/{runId}/run.jsonList workflow runs with optional filters.
Query Parameters:
workflowId (string, optional) — Filter by workflow IDtaskId (string, optional) — Filter by task IDstatus (string, optional) — Filter by status: pending, running, blocked, completed, failedRequest:
# All runs
curl http://localhost:3001/api/workflow-runs
# Runs for a specific workflow
curl "http://localhost:3001/api/workflow-runs?workflowId=feature-dev"
# Runs for a specific task
curl "http://localhost:3001/api/workflow-runs?taskId=US-42"
# Failed runs only
curl "http://localhost:3001/api/workflow-runs?status=failed"
Response:
[
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"workflowVersion": 2,
"taskId": "US-42",
"status": "completed",
"startedAt": "2026-02-09T12:00:00Z",
"completedAt": "2026-02-09T12:45:00Z",
"duration": 2700,
"stepsCompleted": 7,
"stepsTotal": 7
},
{
"id": "run_20260209_def456",
"workflowId": "security-audit",
"workflowVersion": 1,
"status": "running",
"currentStep": "fix",
"startedAt": "2026-02-09T13:00:00Z",
"stepsCompleted": 2,
"stepsTotal": 5
}
]
Status Codes:
200 OK — SuccessPermissions: Filtered by workflow view permissions.
Get full details of a specific workflow run.
Request:
curl http://localhost:3001/api/workflow-runs/run_20260209_abc123
Response:
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"workflowVersion": 2,
"taskId": "US-42",
"status": "completed",
"currentStep": null,
"context": {
"task": { "id": "US-42", "title": "..." },
"plan": { "stories": [...] },
"implement": { "changes": "..." }
},
"startedAt": "2026-02-09T12:00:00Z",
"completedAt": "2026-02-09T12:45:00Z",
"lastCheckpoint": "2026-02-09T12:45:00Z",
"steps": [
{
"stepId": "plan",
"status": "completed",
"agent": "planner",
"sessionKey": "session_xyz",
"startedAt": "2026-02-09T12:00:00Z",
"completedAt": "2026-02-09T12:10:00Z",
"duration": 600,
"retries": 0,
"output": ".veritas-kanban/workflow-runs/run_20260209_abc123/step-outputs/plan.yml"
},
{
"stepId": "implement",
"status": "completed",
"agent": "developer",
"startedAt": "2026-02-09T12:10:00Z",
"completedAt": "2026-02-09T12:35:00Z",
"duration": 1500,
"retries": 1,
"output": ".veritas-kanban/workflow-runs/run_20260209_abc123/step-outputs/implement-0.md",
"loopState": {
"totalIterations": 5,
"currentIteration": 5,
"completedIterations": 5,
"failedIterations": 0
}
}
]
}
Status Codes:
200 OK — Success404 Not Found — Run not found403 Forbidden — No view permissionPermissions: Requires view permission on the workflow.
Get currently running workflow runs only.
Request:
curl http://localhost:3001/api/workflow-runs/active
Response:
[
{
"id": "run_20260209_def456",
"workflowId": "security-audit",
"workflowVersion": 1,
"status": "running",
"currentStep": "fix",
"startedAt": "2026-02-09T13:00:00Z",
"stepsCompleted": 2,
"stepsTotal": 5
}
]
Status Codes:
200 OK — SuccessPermissions: Filtered by workflow view permissions.
Notes: Returns metadata only (not full run state).
Get aggregated workflow statistics for a given period.
Query Parameters:
period (string, optional) — Period for stats: 24h, 7d, 30d (default: 7d)Request:
curl "http://localhost:3001/api/workflow-runs/stats?period=7d"
Response:
{
"period": "7d",
"totalWorkflows": 5,
"activeRuns": 2,
"completedRuns": 42,
"failedRuns": 8,
"avgDuration": 1800000,
"successRate": 0.84,
"perWorkflow": [
{
"workflowId": "feature-dev",
"workflowName": "Feature Development Workflow",
"runs": 25,
"completed": 20,
"failed": 5,
"successRate": 0.8,
"avgDuration": 1800000
},
{
"workflowId": "security-audit",
"workflowName": "Security Audit & Remediation",
"runs": 17,
"completed": 15,
"failed": 2,
"successRate": 0.88,
"avgDuration": 900000
}
]
}
Field Descriptions:
avgDuration — Average duration in millisecondssuccessRate — Decimal (0.0 to 1.0) representing percentageperWorkflow — Per-workflow breakdownStatus Codes:
200 OK — Success400 Bad Request — Invalid period valuePermissions: Filtered by workflow view permissions.
Resume a blocked workflow run (after human approval or escalation).
Request:
curl -X POST http://localhost:3001/api/workflow-runs/run_20260209_abc123/resume \
-H "Content-Type: application/json" \
-d '{
"context": {
"reviewerComments": "Looks good, proceed"
}
}'
Request Body:
{
context?: Record<string, unknown>; // Optional: Additional context for resume
}
Response:
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "running",
"currentStep": "deploy",
...
}
Status Codes:
200 OK — Run resumed successfully400 Bad Request — Run not blocked (current status: running/completed/failed)404 Not Found — Run not found403 Forbidden — No execute permissionPermissions: Requires execute permission on the workflow.
Notes:
blocked can be resumedApprove a gate step (allows workflow to continue).
Request:
curl -X POST http://localhost:3001/api/workflow-runs/run_20260209_abc123/steps/quality-gate/approve
Response:
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "running",
"context": {
"_gateApproval": {
"stepId": "quality-gate",
"approved": true,
"approvedBy": "user-123",
"approvedAt": "2026-02-09T14:00:00Z"
}
},
...
}
Status Codes:
200 OK — Gate approved, run resumed400 Bad Request — Step not awaiting approval or not a gate step404 Not Found — Run or step not found403 Forbidden — No execute permissionPermissions: Requires execute permission on the workflow.
Notes:
type: gate and status failedReject a gate step (marks workflow as failed).
Request:
curl -X POST http://localhost:3001/api/workflow-runs/run_20260209_abc123/steps/quality-gate/reject
Response:
{
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "failed",
"error": "Step quality-gate rejected by user-123",
"completedAt": "2026-02-09T14:00:00Z",
...
}
Status Codes:
200 OK — Gate rejected, run marked as failed400 Bad Request — Step not awaiting approval or not a gate step404 Not Found — Run or step not found403 Forbidden — No execute permissionPermissions: Requires execute permission on the workflow.
Get detailed status of a specific step (useful for parallel sub-steps).
Request:
curl http://localhost:3001/api/workflow-runs/run_20260209_abc123/steps/implement/status
Response:
{
"stepId": "implement",
"status": "completed",
"agent": "developer",
"sessionKey": "session_xyz",
"startedAt": "2026-02-09T12:10:00Z",
"completedAt": "2026-02-09T12:35:00Z",
"duration": 1500,
"retries": 1,
"output": ".veritas-kanban/workflow-runs/run_20260209_abc123/step-outputs/implement-0.md",
"loopState": {
"totalIterations": 5,
"currentIteration": 5,
"completedIterations": 5,
"failedIterations": 0
}
}
Status Codes:
200 OK — Success404 Not Found — Run or step not found403 Forbidden — No view permissionPermissions: Requires view permission on the workflow.
List all tool policies (default + custom).
Request:
curl http://localhost:3001/api/tool-policies
Response:
[
{
"role": "planner",
"allowed": ["read", "web_search", "web_fetch", "browser", "image", "nodes"],
"denied": ["write", "edit", "exec", "message"],
"description": "Analysis and planning — read-only access"
},
{
"role": "developer",
"allowed": ["*"],
"denied": [],
"description": "Feature implementation — full access"
},
{
"role": "custom-auditor",
"allowed": ["read", "web_search"],
"denied": ["exec", "write", "edit"],
"description": "Security auditor — read-only with web access"
}
]
Status Codes:
200 OK — SuccessPermissions: Public (no authentication required).
Get a specific tool policy by role.
Request:
curl http://localhost:3001/api/tool-policies/planner
Response:
{
"role": "planner",
"allowed": ["read", "web_search", "web_fetch", "browser", "image", "nodes"],
"denied": ["write", "edit", "exec", "message"],
"description": "Analysis and planning — read-only access"
}
Status Codes:
200 OK — Success404 Not Found — Policy not foundPermissions: Public.
Create a new custom tool policy.
Request:
curl -X POST http://localhost:3001/api/tool-policies \
-H "Content-Type: application/json" \
-d '{
"role": "custom-auditor",
"allowed": ["read", "web_search", "web_fetch", "browser"],
"denied": ["exec", "write", "edit"],
"description": "Security auditor — read-only with web access"
}'
Request Body:
{
role: string; // Required: role name (alphanumeric + dash/underscore, max 50 chars)
allowed: string[]; // Required: tool names (use '*' for all tools)
denied: string[]; // Required: tool names (takes precedence over allowed)
description: string; // Required: what this role does (max 500 chars)
}
Response:
{
"success": true,
"role": "custom-auditor"
}
Status Codes:
201 Created — Policy created successfully400 Bad Request — Validation error409 Conflict — Role already existsPermissions: Any authenticated user can create custom policies.
Validation:
role: Required, alphanumeric + dash/underscore, max 50 charactersallowed: Required array, max 100 toolsdenied: Required array (can be empty), max 100 toolsdescription: Required, max 500 charactersUpdate an existing tool policy (including defaults).
Request:
curl -X PUT http://localhost:3001/api/tool-policies/custom-auditor \
-H "Content-Type: application/json" \
-d '{
"role": "custom-auditor",
"allowed": ["read", "web_search"],
"denied": ["exec", "write", "edit", "message"],
"description": "Updated auditor policy"
}'
Response:
{
"success": true,
"role": "custom-auditor"
}
Status Codes:
200 OK — Policy updated successfully400 Bad Request — Validation error or role mismatch404 Not Found — Policy not foundPermissions: Any authenticated user can update policies.
Notes: Default policies can be edited but not deleted.
Delete a custom tool policy.
Request:
curl -X DELETE http://localhost:3001/api/tool-policies/custom-auditor
Response:
(Empty body, 204 status)
Status Codes:
204 No Content — Policy deleted successfully400 Bad Request — Cannot delete default policy404 Not Found — Policy not foundPermissions: Any authenticated user can delete custom policies.
Notes: Default policies (planner, developer, reviewer, tester, deployer) cannot be deleted.
Validate if a specific tool is allowed for a role.
Request:
curl -X POST http://localhost:3001/api/tool-policies/planner/validate \
-H "Content-Type: application/json" \
-d '{
"tool": "exec"
}'
Request Body:
{
tool: string; // Required: tool name to validate
}
Response:
{
"role": "planner",
"tool": "exec",
"allowed": false,
"reason": "Tool 'exec' is in the denied list"
}
Status Codes:
200 OK — Success404 Not Found — Policy not foundPermissions: Public.
Workflow agents can set sandboxPresetId to select a sandbox policy preset for
that role. The workflow executor dry-runs the preset against the selected
provider before launching the step. Required unsupported controls block the
step before execution and write a sandbox-policy governance trace; advisory
unsupported controls continue with warnings.
Use /api/sandbox-policies/validate to preflight a workflow agent’s preset in
the authoring UI or custom automation. Public preflights must include a
providerRuntimeManifestDigest currently registered by a connected agent host;
the API rejects stale, disconnected, unknown, or provider-mismatched manifests.
Presets can also be assigned visually in the workflow authoring panel.
Sandbox policies are complementary to tool policies:
Workflow definitions can set config.budget for workflow-wide defaults and
agents[].budget for stricter role-specific caps. Launch callers can also pass
a stricter budget override to POST /api/workflows/:id/runs.
Supported budget limits:
totalTokens, inputTokens, and outputTokenscostUsdtoolCallsruntimeSeconds and idleRuntimeSecondsretriesfanOutBudget evaluation is policy enforcement, not dashboard-only analytics. Soft
thresholds create visible warnings and budget-policy governance traces. Hard
thresholds enforce the configured action:
pause or require-approval blocks the workflow run for operator review.downgrade records a routed decision and applies downgradeModel to Codex
workflow steps.cancel fails the run immediately.The run record includes budget.usage, budget.thresholdEvents, budget.traceIds,
and budget.modelOverride so run detail, timelines, and completion packets can
show exactly what happened.
Get the full dependency graph for a task (recursive tree traversal).
Request:
curl http://localhost:3001/api/tasks/US-42/dependencies
Response:
{
"task": "US-42",
"depends_on": [
{
"id": "US-40",
"title": "Create database schema",
"status": "done",
"depends_on": []
},
{
"id": "US-41",
"title": "Implement auth middleware",
"status": "in-progress",
"depends_on": [
{
"id": "US-39",
"title": "Setup JWT library",
"status": "done",
"depends_on": []
}
]
}
],
"blocks": [
{
"id": "US-43",
"title": "Add user permissions",
"status": "todo",
"blocks": []
}
]
}
Status Codes:
200 OK — Success404 Not Found — Task not found400 Bad Request — Circular dependency detectedNotes:
Add a dependency to a task.
Request:
curl -X POST http://localhost:3001/api/tasks/US-42/dependencies \
-H "Content-Type: application/json" \
-d '{
"dependsOn": "US-40",
"direction": "depends_on"
}'
Request Body:
{
dependsOn: string; // Required: task ID of the dependency
direction: 'depends_on' | 'blocks'; // Required: direction of dependency
}
Status Codes:
200 OK — Dependency added400 Bad Request — Would create circular dependency404 Not Found — Task not foundNotes:
depends_on: This task depends on the specified taskblocks: This task blocks the specified taskRemove a dependency from a task.
Request:
curl -X DELETE http://localhost:3001/api/tasks/US-42/dependencies/US-40?direction=depends_on
Query Parameters:
direction (required): depends_on or blocksStatus Codes:
200 OK — Dependency removed404 Not Found — Task or dependency not foundSave checkpoint state for a task.
Request:
curl -X POST http://localhost:3001/api/tasks/US-42/checkpoint \
-H "Content-Type: application/json" \
-d '{
"state": {
"current_step": 3,
"completed": ["step1", "step2"],
"api_key": "sk-1234567890",
"context": "Working on user authentication"
}
}'
Request Body:
{
state: any; // Required: checkpoint state (auto-sanitized for secrets)
}
Response:
{
"success": true,
"checkpoint": {
"taskId": "US-42",
"state": {
"current_step": 3,
"completed": ["step1", "step2"],
"api_key": "[REDACTED]",
"context": "Working on user authentication"
},
"createdAt": "2026-02-15T12:00:00Z",
"expiresAt": "2026-02-16T12:00:00Z",
"resumeCount": 0
}
}
Status Codes:
200 OK — Checkpoint saved400 Bad Request — State exceeds 1MB limit404 Not Found — Task not foundNotes:
Resume checkpoint state for a task.
Request:
curl http://localhost:3001/api/tasks/US-42/checkpoint
Response:
{
"success": true,
"checkpoint": {
"taskId": "US-42",
"state": {
"current_step": 3,
"completed": ["step1", "step2"],
"api_key": "[REDACTED]",
"context": "Working on user authentication"
},
"createdAt": "2026-02-15T12:00:00Z",
"expiresAt": "2026-02-16T12:00:00Z",
"resumeCount": 1
}
}
Status Codes:
200 OK — Checkpoint retrieved (increments resumeCount)404 Not Found — Task or checkpoint not found410 Gone — Checkpoint expiredNotes:
Clear checkpoint state for a task.
Request:
curl -X DELETE http://localhost:3001/api/tasks/US-42/checkpoint
Status Codes:
200 OK — Checkpoint cleared404 Not Found — Task or checkpoint not foundAdd an observation to a task.
Request:
curl -X POST http://localhost:3001/api/observations \
-H "Content-Type: application/json" \
-d '{
"taskId": "US-42",
"type": "decision",
"content": "Chose React Query over Redux for simpler data fetching and better caching",
"importance": 8
}'
Request Body:
{
taskId: string; // Required: task ID
type: 'decision' | 'blocker' | 'insight' | 'context'; // Required
content: string; // Required: observation text (XSS-sanitized)
importance: number; // Required: 1-10 (1-3: low, 4-7: medium, 8-10: high)
}
Response:
{
"success": true,
"observation": {
"id": "obs_abc123",
"taskId": "US-42",
"type": "decision",
"content": "Chose React Query over Redux for simpler data fetching and better caching",
"importance": 8,
"createdAt": "2026-02-15T12:00:00Z",
"createdBy": "veritas"
}
}
Status Codes:
201 Created — Observation added400 Bad Request — Invalid type or importance score404 Not Found — Task not foundNotes:
Get all observations for a task.
Request:
curl http://localhost:3001/api/tasks/US-42/observations
Response:
{
"success": true,
"observations": [
{
"id": "obs_abc123",
"taskId": "US-42",
"type": "decision",
"content": "Chose React Query over Redux",
"importance": 8,
"createdAt": "2026-02-15T12:00:00Z",
"createdBy": "veritas"
},
{
"id": "obs_def456",
"taskId": "US-42",
"type": "blocker",
"content": "Waiting on API key from ops team",
"importance": 6,
"createdAt": "2026-02-15T13:00:00Z",
"createdBy": "codex"
}
]
}
Status Codes:
200 OK — Success404 Not Found — Task not foundFull-text search across all observations for all tasks.
Request:
curl "http://localhost:3001/api/observations/search?query=react+query&limit=10&offset=0"
Query Parameters:
query (required): search terms (full-text search)limit (optional): max results per page (default: 50, max: 200)offset (optional): pagination offset (default: 0)Response:
{
"success": true,
"results": [
{
"id": "obs_abc123",
"taskId": "US-42",
"taskTitle": "Implement user authentication",
"type": "decision",
"content": "Chose React Query over Redux for simpler data fetching",
"importance": 8,
"createdAt": "2026-02-15T12:00:00Z",
"createdBy": "veritas"
}
],
"total": 1,
"limit": 10,
"offset": 0
}
Status Codes:
200 OK — Success400 Bad Request — Missing query or invalid limit/offsetNotes:
Delete an observation.
Request:
curl -X DELETE http://localhost:3001/api/observations/obs_abc123
Status Codes:
200 OK — Observation deleted404 Not Found — Observation not foundNotes:
Filter tasks by assigned agent name.
Request:
curl "http://localhost:3001/api/tasks?agent=codex"
Query Parameters:
agent (optional): agent name (trimmed, max 100 chars)status (optional): filter by status (todo, in-progress, blocked, done)limit (optional): max results (default: 100)offset (optional): pagination offset (default: 0)Response:
{
"success": true,
"tasks": [
{
"id": "US-42",
"title": "Implement user authentication",
"status": "in-progress",
"agents": ["codex"],
"priority": "high",
"type": "feature"
},
{
"id": "US-45",
"title": "Add input validation",
"status": "todo",
"agents": ["codex", "veritas"],
"priority": "medium",
"type": "code"
}
],
"total": 2
}
Status Codes:
200 OK — SuccessNotes:
agents[] arrayAll workflow state changes are broadcast via WebSocket for real-time UI updates.
Connection:
const ws = new WebSocket('ws://localhost:3001/ws');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log('Event:', message.type, message.data);
};
Emitted when a workflow run starts.
Payload:
{
"type": "workflow:started",
"data": {
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "running",
"startedAt": "2026-02-09T12:00:00Z",
...
}
}
Emitted when a step begins execution.
Payload:
{
"type": "workflow:step:started",
"data": {
"runId": "run_20260209_abc123",
"stepId": "plan",
"status": "running",
"startedAt": "2026-02-09T12:00:00Z"
}
}
Emitted when a step completes successfully.
Payload:
{
"type": "workflow:step:completed",
"data": {
"runId": "run_20260209_abc123",
"stepId": "plan",
"status": "completed",
"completedAt": "2026-02-09T12:10:00Z",
"duration": 600
}
}
Emitted when a step fails.
Payload:
{
"type": "workflow:step:failed",
"data": {
"runId": "run_20260209_abc123",
"stepId": "plan",
"status": "failed",
"error": "Acceptance criteria not met",
"completedAt": "2026-02-09T12:10:00Z"
}
}
Emitted when a workflow run completes (all steps succeeded).
Payload:
{
"type": "workflow:completed",
"data": {
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "completed",
"completedAt": "2026-02-09T12:45:00Z",
"duration": 2700
}
}
Emitted when a workflow run fails (step failed with no retry policy).
Payload:
{
"type": "workflow:failed",
"data": {
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "failed",
"error": "Step 'test' failed after 2 retries",
"completedAt": "2026-02-09T12:30:00Z"
}
}
Emitted when a workflow run is blocked (waiting for human approval or gate).
Payload:
{
"type": "workflow:blocked",
"data": {
"id": "run_20260209_abc123",
"workflowId": "feature-dev",
"status": "blocked",
"currentStep": "quality-gate",
"error": "Quality gate failed — manual review required"
}
}
Emitted when a task associated with a workflow run is updated (triggers counter/metrics refresh).
Payload:
{
"type": "task:changed",
"data": {
"taskId": "US-42",
"status": "done",
"workflowRunId": "run_20260209_abc123"
}
}
Emitted when an agent’s status changes (for multi-agent coordination).
Payload:
{
"type": "agent:status",
"data": {
"agent": "developer",
"status": "working",
"taskTitle": "Implement feature X",
"workflowRunId": "run_20260209_abc123"
}
}
export interface WorkflowDefinition {
id: string;
name: string;
version: number;
description: string;
config?: WorkflowConfig;
agents: WorkflowAgent[];
steps: WorkflowStep[];
variables?: Record<string, unknown>;
schemas?: Record<string, unknown>;
}
export interface WorkflowConfig {
timeout?: number; // seconds
fresh_session_default?: boolean;
progress_file?: string;
telemetry_tags?: string[];
budget?: AgentBudgetPolicy;
}
export interface WorkflowAgent {
id: string;
name: string;
role: string; // maps to tool policy
sandboxPresetId?: string; // maps to a sandbox policy preset
budget?: AgentBudgetPolicy; // stricter workflow-agent budget
model?: string; // default model for this agent
description: string;
tools?: string[]; // tool restrictions (overrides role policy)
}
export type StepType = 'agent' | 'loop' | 'gate' | 'parallel';
export interface WorkflowStep {
id: string;
name: string;
agent?: string; // agent ID (required for agent/loop steps)
type: StepType;
fresh_session?: boolean; // legacy: use session config instead
session?: StepSessionConfig;
input?: string; // template for agent prompt
output?: StepOutput;
acceptance_criteria?: string[];
on_fail?: FailurePolicy;
timeout?: number;
// Loop-specific config
loop?: LoopConfig;
// Gate-specific config
condition?: string; // expression evaluating to boolean
on_false?: EscalationPolicy;
// Parallel-specific config
parallel?: ParallelConfig;
}
export interface StepOutput {
file: string; // filename in step-outputs/
schema?: string; // schema ID for validation
}
export interface FailurePolicy {
retry?: number;
retry_delay_ms?: number; // delay between retries
retry_step?: string; // retry a different step ID
escalate_to?: 'human' | `agent:${string}` | 'skip';
escalate_message?: string;
on_exhausted?: EscalationPolicy;
}
export interface EscalationPolicy {
escalate_to: 'human' | `agent:${string}` | 'skip';
escalate_message?: string;
}
export interface LoopConfig {
over: string; // expression returning array
item_var?: string; // variable name for current item
index_var?: string; // variable name for loop index
completion: 'all_done' | 'any_done' | 'first_success';
fresh_session_per_iteration?: boolean;
verify_each?: boolean;
verify_step?: string; // step ID to run after each iteration
max_iterations?: number;
continue_on_error?: boolean; // if true, failed iterations don't fail the loop
}
export interface GateStepConfig {
condition: string; // expression evaluating to boolean
on_false: EscalationPolicy;
}
export interface ParallelConfig {
steps: ParallelSubStep[]; // sub-steps to execute in parallel
completion: 'all' | 'any' | number; // wait for all, any, or N sub-steps
fail_fast?: boolean; // if true, abort others when one fails
timeout?: number; // max time to wait (seconds)
}
export interface ParallelSubStep {
id: string;
agent: string;
input: string; // template for sub-step input
output?: StepOutput;
timeout?: number;
}
export type WorkflowRunStatus = 'pending' | 'running' | 'blocked' | 'completed' | 'failed';
export type WorkflowAdmissionState = 'waiting' | 'dispatching' | 'active' | 'terminal';
export interface WorkflowRun {
id: string; // run_<timestamp>_<nanoid>
workflowId: string;
workflowVersion: number;
taskId?: string; // optional task association
admission?: WorkflowRootAdmissionBinding; // durable root reservation or queue identity
status: WorkflowRunStatus;
currentStep?: string; // current step ID
context: Record<string, unknown>; // shared context across steps
budget?: AgentBudgetState; // effective budget, usage, threshold events, traces
startedAt: string;
completedAt?: string;
lastCheckpoint?: string; // last state persistence timestamp
error?: string;
steps: StepRun[];
}
export type StepRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
export interface StepRun {
stepId: string;
status: StepRunStatus;
agent?: string;
sessionKey?: string; // OpenClaw session key
startedAt?: string;
completedAt?: string;
duration?: number; // seconds
retries: number;
output?: string; // path to output file
error?: string;
admission?: WorkflowStepAdmissionBinding; // latest executable attempt or queue decision
// Loop-specific state
loopState?: {
totalIterations: number;
currentIteration: number;
completedIterations: number;
failedIterations: number;
};
}
When root or step capacity is temporarily unavailable, the corresponding
admission binding has state: "waiting" and a durable queueEntryId. The run
and step remain pending; provider execution is not marked active. A claimed
entry briefly uses dispatching while Veritas transfers durable ownership to
workflow recovery, then becomes active before provider execution.
export interface ToolPolicy {
role: string;
allowed: string[]; // tool names (use '*' for all)
denied: string[]; // tool names (takes precedence)
description: string;
}
export interface StepSessionConfig {
mode: 'fresh' | 'reuse'; // fresh = new session, reuse = continue existing
context: 'minimal' | 'full' | 'custom'; // how much context to pass
cleanup: 'delete' | 'keep'; // delete session after step or keep
timeout: number; // session timeout in seconds
includeOutputsFrom?: string[]; // step names for 'custom' context
}
export type WorkflowPermission = 'view' | 'create' | 'edit' | 'delete' | 'execute';
export interface WorkflowACL {
workflowId: string;
owner: string; // user ID or 'system'
editors: string[]; // users who can edit
viewers: string[]; // users who can view
executors: string[]; // users who can trigger runs
isPublic: boolean; // anyone can view/execute
}
export interface WorkflowAuditEvent {
timestamp: string;
userId: string;
action: 'create' | 'edit' | 'delete' | 'run';
workflowId: string;
workflowVersion?: number;
changes?: Array<{ field: string; oldValue: unknown; newValue: unknown }>;
runId?: string;
}
All errors follow this structure:
{
"error": {
"message": "Human-readable error message",
"code": "ERROR_CODE",
"details": {}
}
}
| Code | Name | Description |
|---|---|---|
400 |
Bad Request | Validation error, missing required fields, invalid input |
401 |
Unauthorized | Missing or invalid authentication |
403 |
Forbidden | No permission to perform action |
404 |
Not Found | Resource not found |
409 |
Conflict | Resource already exists (e.g., workflow ID collision) |
500 |
Internal Server Error | Unexpected server error |
{
"error": {
"message": "Workflow must define at least one agent",
"code": "VALIDATION_ERROR",
"details": {
"field": "agents"
}
}
}
{
"error": {
"message": "Workflow feature-dev not found",
"code": "NOT_FOUND",
"details": {
"workflowId": "feature-dev"
}
}
}
{
"error": {
"message": "No edit permission for workflow feature-dev",
"code": "PERMISSION_DENIED",
"details": {
"workflowId": "feature-dev",
"requiredPermission": "edit",
"userId": "user-123"
}
}
}
{
"error": {
"message": "Workflow hello-world already exists",
"code": "CONFLICT",
"details": {
"workflowId": "hello-world"
}
}
}
End of API Reference