Reference
API reference
A JSON REST API under /api plus one WebSocket. Ids are 12-character hex strings, timestamps are ISO 8601 UTC with a Z suffix, and every mutation is capability-checked and audit-logged.
Base URL in development: http://localhost:8000. Interactive OpenAPI docs are served by the backend at http://localhost:8000/docs (schema at /openapi.json). Every path below is relative to the base URL, and — unless it is auth-exempt — passes through the authentication and role-based access-control middleware described next.
Authentication & errors
Every request under /api resolves an actor and that actor's effective permissions. The middleware checks credentials in strict precedence order:
- X-API-Key header — highest precedence. A key linked to a member acts with that member's effective permissions; an unlinked key acts with its own plain role. A missing or revoked key is rejected with
401. - Authorization: Bearer <session token> — a token minted by
/api/auth/login. The request acts as that member; an expired token or a non-active member returns401. - No credential — in
AUTH_MODE=open(the default for local development) the request is treated as the workspace owner, with full admin permissions. InAUTH_MODE=requiredit is rejected with401.
# 1. API key — acts as the linked member, or the key's own role
$ curl http://localhost:8000/api/workflows -H "X-API-Key: aos_1c3f9a…"
# 2. Session bearer token from POST /api/auth/login
$ curl http://localhost:8000/api/workflows -H "authorization: Bearer eyJhbGciOi…"
# 3. No credential — only works in AUTH_MODE=open (acts as the owner);
# AUTH_MODE=required returns 401 { "detail": "Authentication required" }
$ curl http://localhost:8000/api/workflows
Four paths bypass auth entirely (they are how you obtain a credential or probe liveness): /api/health, /api/auth/login, /api/auth/signup, and /api/auth/accept-invite. Every path under /api/hooks/ is also exempt — there, the per-workflow secret embedded in the URL is the credential.
Capabilities
Once the actor is known, the middleware derives the single capability the route requires from its method and path, and returns 403 if the credential lacks it. Roles carry default capabilities; per-member overrides adjust them in either direction. Every endpoint below names its required capability.
| Field | Type | Default | Description |
|---|---|---|---|
| view | all roles | admin · editor · viewer | Read anything — every GET, plus /validate and the MCP entrypoint. |
| run_workflows | editor + | admin · editor | Trigger runs via POST /api/workflows/{id}/runs. |
| approve_gates | editor + | admin · editor | Resolve paused human gates via POST /api/runs/{id}/approve. |
| cancel_resume | editor + | admin · editor | POST /api/runs/{id}/cancel and /api/runs/{id}/resume. |
| create_edit_workflows | editor + | admin · editor | Create & edit workflows, checks, tools, schedules, webhooks, templates. |
| publish_workflows | editor + | admin · editor | Reserved; the update that sets status:published is gated on create_edit_workflows. |
| delete_workflows | admin | admin | DELETE /api/workflows/{id}. |
| manage_members | admin | admin | Invite, update, approve, remove members and edit their permissions. |
| manage_api_keys | admin | admin | Create and revoke API keys. |
| manage_workspace | admin | admin | Rename the workspace and change its plan. |
Error envelope & status codes
Every error — validation, auth, quota, or not-found — uses FastAPI's envelope: a single detail string.
{ "detail": "Workflow not found" }
{ "detail": "Workflow graph invalid: Node 'x' uses unknown tool 'custom:foo'" }
{ "detail": "This action requires the 'delete_workflows' permission" }
{ "detail": "Monthly run limit reached (1,000/1,000 on the free plan) — upgrade to keep running" }
| Field | Type | Default | Description |
|---|---|---|---|
| 400 | Bad Request | — | Invalid graph or checks, bad cron, malformed body, duplicate tool name, or a last-admin guard. |
| 401 | Unauthorized | — | Missing / invalid API key, or an expired session token (or no credential in required mode). |
| 402 | Payment Required | — | Monthly run quota reached. Returned by run triggers (REST, webhook, MCP). |
| 403 | Forbidden | — | Authenticated, but the credential lacks the required capability. |
| 404 | Not Found | — | Unknown id — or a wrong webhook id/secret, deliberately indistinguishable. |
| 429 | Too Many Requests | — | Per-IP rate limit. Off by default; enabled with RATE_LIMIT_PER_MINUTE. |
Conventions
| Field | Type | Default | Description |
|---|---|---|---|
| ids | string | — | 12-character hex, unique per resource. |
| timestamps | string | — | ISO 8601 UTC with a Z suffix, e.g. 2026-07-05T14:10:05Z. |
| pagination | limit / offset | 50 / 0 | Only /api/runs and /api/audit paginate — {runs|logs, total}. limit caps at 200. Other lists return the full array. |
| Idempotency-Key | header | unset | On POST /api/workflows/{id}/runs: a repeated key returns the original run instead of starting a duplicate. |
| Content-Type | header | application/json | All request and response bodies are JSON. |
Run lifecycle
A run moves pending → running → a terminal success or failed, or parks at paused when it reaches a human gate. Individual steps carry their own status: running, success, failed, or waiting (a gate awaiting review). Failed runs keep their last checkpoint and are resumable; paused runs continue when a gate is approved.
Workflows
/api/workflowsList workflows, newest-updated first, each with its embedded checks and aggregate run_stats. Capability: view. Not paginated.
[
{
"id": "9bc62f209c52",
"name": "Support triage",
"description": "Classify and reply to inbound tickets.",
"status": "published",
"version": 2,
"graph": { "nodes": [ … ], "edges": [ … ] },
"checks": [
{ "id": "c1a2", "name": "No PII in reply", "type": "not_contains",
"target": "reply", "value": "SSN", "case_sensitive": false }
],
"run_stats": { "total_runs": 6, "success_rate": 83.3, "avg_cost_usd": 0.004083 },
"created_at": "2026-07-05T13:49:20Z",
"updated_at": "2026-07-05T14:02:11Z"
}
]
/api/workflowsCreate a workflow. The graph is optional — an empty workflow is a valid draft. Capability: create_edit_workflows. Returns 201 with the full workflow.
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | required | Human-readable workflow name. |
| description | string | "" | Optional prose description. |
| graph | Graph | { nodes: [], edges: [] } | Nodes and edges. Node types: agent, tool, condition, human_gate, loop, merge. |
{ "name": "Support triage", "description": "Classify and reply.", "graph": { "nodes": [], "edges": [] } }
/api/workflows/{id}Fetch one workflow with its full graph, checks, and run_stats. Capability: view. 404 if unknown.
/api/workflows/{id}Update name, description, status, and/or graph. Capability: create_edit_workflows. Any graph change snapshots the previous version and bumps the version number.
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | null | unchanged | New name. |
| description | string | null | unchanged | New description. |
| status | "draft" | "published" | null | unchanged | Publish or unpublish. published workflows count toward active_workflows metrics. |
| graph | Graph | null | unchanged | Replacing the graph snapshots the prior graph as an immutable version and increments version. |
{ "status": "published" }
// or replace the graph (this bumps version):
{ "graph": { "nodes": [ … ], "edges": [ … ] } }
/api/workflows/{id}Delete the workflow and its runs, steps, checkpoints, and schedules. Capability: delete_workflows. Returns 204.
/api/workflows/{id}/validateStructural and expression validation without running. Capability: view (read-only despite being a POST). Errors block runs; warnings do not.
{
"valid": false,
"errors": ["Edge 'e1' references missing target node 'ghost'"],
"warnings": ["Condition node 'check' has no 'false' branch — runs end there when false"]
}
/api/workflows/{id}/checksReplace the workflow's evaluation checks (does not version the graph). Capability: create_edit_workflows. Invalid checks return 400.
Check types: contains, not_contains, equals, regex, max_cost_usd, max_duration_ms, and expression. Each is scored against the final state after a successful run; results appear as evals on the run.
{
"checks": [
{ "name": "No PII in reply", "type": "not_contains", "target": "reply", "value": "SSN" },
{ "name": "Under budget", "type": "max_cost_usd", "value": 0.05 }
]
}
/api/workflows/{id}/versionsImmutable graph snapshots, newest first — one entry per graph save. Capability: view.
[ { "id": "1de2a9c04b11", "version": 1, "graph": { … }, "created_at": "2026-07-05T13:49:20Z" } ]
Webhook management
A workflow can expose an inbound webhook. These three endpoints manage it; the inbound trigger itself is documented under Webhook triggers.
/api/workflows/{id}/webhookEnable the inbound webhook, or rotate its secret if already enabled. Capability: create_edit_workflows. Returns { enabled, url }.
{ "enabled": true, "url": "http://localhost:8000/api/hooks/9bc62f209c52/4f7c…e21a" }
/api/workflows/{id}/webhookCurrent webhook state. Capability: view. url is null while disabled.
/api/workflows/{id}/webhookDisable the webhook — the old URL stops working immediately. Capability: create_edit_workflows. Returns 204.
Trigger a run
/api/workflows/{id}/runsTrigger a run. Capability: run_workflows. Checks quota (402), then validates the graph (400 on errors), then returns 201 immediately — execution is asynchronous.
| Field | Type | Default | Description |
|---|---|---|---|
| input | object | {} | Run input variables. Merged into initial run state and available as {placeholders} and to expressions. |
Send an Idempotency-Key header to make the trigger safe to retry: a repeated key returns the original run instead of starting a duplicate. The response is a RunOut in pending status — subscribe to the event stream or poll GET /api/runs/{id} to follow it.
$ curl -s -X POST http://localhost:8000/api/workflows/9bc62f209c52/runs \
-H "content-type: application/json" \
-H "Idempotency-Key: ticket-8841-triage" \
-d '{"input": {"ticket": "I was charged twice for my March invoice"}}'
// → 201
{ "id": "af772b3602ef", "workflow_id": "9bc62f209c52", "status": "pending",
"input": { "ticket": "I was charged twice…" }, "cost_usd": 0.0, "created_at": "2026-07-05T14:10:05Z", … }
Runs
/api/runsPaginated history, newest first. Capability: view. Filters: workflow_id, status. Params: limit (max 200), offset. Steps are omitted in list responses.
| Field | Type | Default | Description |
|---|---|---|---|
| workflow_id | string | all | Restrict to one workflow. |
| status | string | all | pending · running · paused · success · failed. |
| limit | int | 50 | Page size, capped at 200. |
| offset | int | 0 | Rows to skip. |
{
"runs": [
{ "id": "af772b3602ef", "workflow_id": "9bc62f209c52", "workflow_name": "Support triage",
"status": "paused", "cost_usd": 0.0038, "duration_ms": null, "steps": [],
"pending_gate": { "node_id": "review", "prompt": "Review the drafted reply…" },
"created_at": "2026-07-05T14:10:05Z" }
],
"total": 41
}
/api/runs/{id}Full run detail: status, input/output, cost, duration, error, pending gate, eval results, and every step with its own input, output, cost, and timing. Capability: view.
{
"id": "af772b3602ef",
"workflow_id": "9bc62f209c52",
"workflow_name": "Support triage",
"status": "paused",
"input": { "ticket": "I was charged twice…" },
"output": null,
"cost_usd": 0.0038,
"duration_ms": null,
"error_message": null,
"pending_gate": { "node_id": "review", "prompt": "Review the drafted reply…" },
"evals": null,
"created_at": "2026-07-05T14:10:05Z",
"steps": [
{ "node_id": "classify", "node_type": "agent", "label": "Classify Ticket",
"status": "success", "cost_usd": 0.0021,
"input": null, "output": { "text": "billing", "model": "claude-…", "input_tokens": 220, "output_tokens": 4 },
"started_at": "2026-07-05T14:10:05Z", "finished_at": "2026-07-05T14:10:06Z", "error": null },
{ "node_id": "review", "node_type": "human_gate", "label": "Review Reply",
"status": "waiting", "input": { "prompt": "Review the drafted reply…" }, "cost_usd": 0.0 }
]
}
A successful run also carries an evals summary when the workflow has checks: { "passed": 2, "total": 2, "results": [ … ] }.
/api/runs/{id}/approveResolve a paused human gate. Capability: approve_gates. 400 unless the run is paused on a gate.
| Field | Type | Default | Description |
|---|---|---|---|
| approved | boolean | required | true resumes past the gate; false fails the run with 'Rejected at human gate …'. |
| input | object | null | null | Merged into run state on approval — inject a correction or a reviewer decision. |
Approval records the decision in state under approval_<node_id>, marks the gate completed, rewrites the checkpoint, and resumes execution. Rejection settles the run as failed.
{ "approved": true, "input": { "reply_tone": "formal" } }
/api/runs/{id}/resumeRe-run a failed run from its last checkpoint — completed nodes never re-execute. Capability: cancel_resume. 400 if the run is not failed (paused runs use /approve) or has no checkpoint.
/api/runs/{id}/cancelCancel a pending, running, or paused run. Capability: cancel_resume. Running work is interrupted mid-node; the run settles as failed with 'Cancelled by user' and keeps its checkpoints. 400 if already finished.
/api/runs/{id}/streamServer-push WebSocket of run events — step lifecycle, pauses, completion, evaluation. Connect while a run is active.
Every event payload, the required ?token= query parameter in required mode, and a full client example live on Events & streaming.
Templates
/api/templatesThe seeded template gallery — complete graphs ready to instantiate, sorted by name. Capability: view.
[ { "id": "3f9a01d2c7b4", "name": "Support Triage Crew", "category": "Support",
"description": "Classify, draft, and gate replies.", "graph": { "nodes": [ … ], "edges": [ … ] } } ]
/api/templates/{id}/instantiateCreate a workflow from a template, copying its graph. Capability: create_edit_workflows. Body: { name? }. Returns 201 with the new workflow.
{ "name": "Triage — EU region" } // optional; defaults to the template name
Custom tools
Registered tools are addressable from a workflow's tool nodes as custom:<name>. See the Tool reference for node wiring.
/api/toolsRegistered tools, sorted by name. Capability: view.
/api/toolsRegister an external endpoint (kind http) or a remote MCP tool (kind mcp) as custom:<name>. Capability: create_edit_workflows. Duplicate name → 400; kind mcp without mcp_tool → 400.
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | required | Lowercase slug ^[a-z0-9][a-z0-9-_]{1,40}$. Referenced as custom:<name>. |
| label | string | = name | Display label. |
| description | string | "" | What the tool does. |
| kind | "http" | "mcp" | "http" | http calls endpoint_url; mcp calls a remote MCP server's tool. |
| endpoint_url | string | required | HTTP endpoint (http) or MCP server URL (mcp). |
| method | string | "POST" | HTTP verb; uppercased on save. |
| headers | object | {} | Static request headers, e.g. auth. |
| mcp_tool | string | null | null | Required for kind mcp — the remote tool name to call. |
| input_schema | object | { type: object } | JSON Schema validated on /test and at run time. |
| timeout_seconds | int | 60 | Per-call timeout, 1–600. |
| retry_count | int | 1 | Retries on failure, 0–5. |
{
"name": "score-lead",
"label": "Score lead",
"description": "Scores an inbound lead 0-100.",
"kind": "http",
"endpoint_url": "https://my-agent.example.com/score",
"method": "POST",
"headers": { "Authorization": "Bearer …" },
"input_schema": {
"type": "object",
"properties": { "company": { "type": "string" } },
"required": ["company"]
},
"timeout_seconds": 60,
"retry_count": 1
}
/api/tools/{id}Fetch one tool registration. Capability: view.
/api/tools/{id}Update any registration field (partial). Capability: create_edit_workflows. Workflows referencing custom:<name> pick the change up on their next run.
/api/tools/{id}Remove a tool. Capability: create_edit_workflows. Returns 204. Workflows still referencing it fail validation before their next run.
/api/tools/{id}/testInvoke the tool once with sample arguments — schema validation included. Capability: create_edit_workflows. Always 200; success is reported in the body's ok field.
{ "arguments": { "company": "Acme" } }
// → 200 on success
{ "ok": true, "output": { "score": 87 }, "duration_ms": 214 }
// → 200 on failure (ok:false, with the error message)
{ "ok": false, "error": "HTTP 500 from https://my-agent.example.com/score", "duration_ms": 512 }
Schedules
Cron-based triggers, evaluated in UTC by an in-process scheduler (~20s tick). Full syntax and execution semantics live on Scheduled triggers. All five endpoints require create_edit_workflows to mutate and view to read.
/api/workflows/{id}/schedulesCreate a schedule: { cron, input? }. Capability: create_edit_workflows. Invalid cron → 400 naming the field problem. Returns 201 with the schedule and its computed next_run_at.
{ "cron": "0 9 * * 1-5", "input": { "source": "daily-digest" } }
// → 201
{ "id": "b8d1f2a90c34", "workflow_id": "9bc62f209c52", "cron": "0 9 * * 1-5",
"input": { "source": "daily-digest" }, "enabled": true,
"last_run_at": null, "next_run_at": "2026-07-07T09:00:00Z", "created_at": "2026-07-06T14:21:08Z" }
/api/workflows/{id}/schedulesSchedules attached to one workflow, newest first. Capability: view.
/api/schedulesEvery schedule in the workspace — the fleet view. Capability: view.
/api/schedules/{id}Update cron, input, or enabled. Capability: create_edit_workflows. Editing the cron recomputes next_run_at; disabling pauses firing without deleting.
/api/schedules/{id}Delete the schedule; runs it already started are unaffected. Capability: create_edit_workflows. Returns 204.
Workspace & team
Workspace & plan
/api/workspaceWorkspace name, plan, member_count, and created_at. Capability: view.
{ "id": "0a11b2c3d4e5", "name": "Acme Ops", "plan": "pro", "member_count": 7, "created_at": "2026-05-01T09:00:00Z" }
/api/workspaceRename the workspace: { name }. Capability: manage_workspace.
/api/workspace/planSwitch plan tiers: { plan: free | starter | pro | enterprise }. Capability: manage_workspace. Quotas and retention apply immediately; run triggers past the monthly quota then return 402.
Members
/api/membersAll members, oldest first. Capability: view. invite_code is present only while status is 'invited'.
[ { "id": "77aa11bb22cc", "name": "Dana Lee", "email": "dana@acme.com",
"role": "editor", "status": "active", "invite_code": null, "joined_at": "2026-05-02T10:11:00Z" } ]
/api/members/inviteInvite a member: { email, name?, role? }. Capability: manage_members. Returns 201 with an invite_code (emailed when Resend is configured). Duplicate email → 400.
{ "email": "sam@acme.com", "name": "Sam Ortiz", "role": "editor" }
/api/members/{id}One member with the full capability matrix (default, override, effective per capability) and api_key_count. Capability: view.
{
"id": "77aa11bb22cc", "name": "Dana Lee", "email": "dana@acme.com", "role": "editor", "status": "active",
"api_key_count": 2,
"permissions": [
{ "capability": "run_workflows", "label": "Run workflows", "default": true, "override": null, "effective": true },
{ "capability": "delete_workflows", "label": "Delete workflows", "default": false, "override": true, "effective": true }
]
}
/api/members/{id}Change a member's role: { role }. Capability: manage_members. Demoting or removing the last active admin is refused with 400.
/api/members/{id}/permissionsSet per-capability overrides: { overrides: { capability: true | false | null } }. Capability: manage_members. null clears an override; unknown capabilities → 400.
{ "overrides": { "delete_workflows": true, "publish_workflows": null } }
/api/members/{id}/statsPer-member activity: runs_triggered, approvals, rejections, workflows_created, cost_usd, a 30-day activity_by_day series, and recent_actions. Capability: view.
/api/members/{id}/approveApprove a self-signup awaiting review — flips status pending → active. Capability: manage_members. 400 unless the member is pending.
/api/members/{id}Remove a member. Capability: manage_members. Returns 204. The last active admin cannot be removed (400).
API keys
/api/keysAll keys, newest first, with a masked prefix, role, linked member_id, last_used_at, and revoked flag. Capability: view. The full secret is never returned here.
/api/keysMint a key: { name, role?, member_id? }. Capability: manage_api_keys. A member_id links the key to that member's effective permissions; otherwise the plain role applies. Returns 201 with the secret — shown exactly once.
{ "name": "CI pipeline", "role": "editor" }
// → 201 — copy the secret now; it is not retrievable again
{ "id": "aa00bb11cc22", "name": "CI pipeline", "prefix": "aos_1c3f9a2b…", "role": "editor",
"member_id": null, "revoked": false, "last_used_at": null, "created_at": "2026-07-06T15:00:00Z",
"secret": "aos_1c3f9a2b7d8e0f1a2b3c4d5e6f708192a3b4c5d6" }
/api/keys/{id}Revoke a key (soft delete — the row stays, revoked becomes true). Capability: manage_api_keys. Returns 204. Subsequent requests with that key get 401.
Usage
/api/usageCurrent billing-month usage: plan, runs_used, runs_limit (null on unlimited plans), cost_usd, and the period window. Capability: view.
{ "plan": "pro", "runs_used": 3120, "runs_limit": 50000, "cost_usd": 42.19,
"period_start": "2026-07-01T00:00:00Z", "period_end": "2026-07-31T23:59:59Z" }
Audit & metrics
/api/auditAppend-only audit trail, newest first. Capability: view. Params: limit (max 200), offset. Each entry carries the actor, action, target, and a details object.
Actions include create, update, delete, run, approve, reject, resume, cancel, webhook_run, schedule_run, plan_change, and the auth and member events.
{
"logs": [
{ "id": "77b21c09aa04", "action": "approve", "actor_id": "77aa11bb22cc", "actor_name": "Dana Lee",
"target_type": "run", "target_id": "af772b3602ef", "details": { "node_id": "review" },
"created_at": "2026-07-05T14:12:40Z" }
],
"total": 128
}
/api/metrics/overviewDashboard aggregates. Capability: view. total_runs, success_rate (of finished runs), total_cost_usd, active_workflows (published), a 14-day runs_by_day series, and eval_pass_rate / evaluated_runs.
{
"total_runs": 412, "success_rate": 91.2, "total_cost_usd": 6.84, "active_workflows": 5,
"runs_by_day": [ { "date": "2026-07-05", "count": 31, "cost_usd": 0.412 } ],
"eval_pass_rate": 96.7, "evaluated_runs": 240
}
/api/healthLiveness probe. Auth-exempt. Returns { "status": "ok", "service": "ballast" }.
Governance & budgets
Runtime policy controls and their inspectable event stream. Every execution-time decision — a denied tool, a redaction, a blocked model or fallback, a budget block, an override — is recorded as a governance event, distinct from the tamper-evident audit log of API mutations.
/api/governance/eventsThe governance / policy event stream — every execution-time policy decision, most recent first. Filter by run_id, workflow_id, event_type, decision, and limit.
| Field | Type | Default | Description |
|---|---|---|---|
| run_id | query | — | Only events for this run. |
| workflow_id | query | — | Only events for this workflow. |
| event_type | query | — | policy_denied · tool_argument_blocked · data_redacted · budget_blocked · model_fallback_blocked · approval_override_granted. |
| decision | query | — | denied · redacted · blocked · granted · allowed. |
| limit | query | 200 | Max rows (capped at 1000). |
{
"id": "ev_7c1a…", "event_type": "data_redacted", "decision": "redacted",
"policy": "dlp", "workflow_id": "9bc62f209c52", "version": 3,
"run_id": "af772b3602ef", "node_id": "classify", "actor_id": null,
"reason": "redacted 2 sensitive value(s) before the prompt left the box",
"evidence": { "redacted_count": 2 }, "created_at": "2026-07-13T18:04:11Z"
}
/api/workflows/{id}/webhook/configHarden a workflow's inbound webhook: require_signature (HMAC), timestamp_tolerance_seconds (replay window), dedup_window_seconds (ingest de-dup), event_id_field. See the Webhooks guide.
/api/runs/{id}/budget-overrideGrant an audited override of the per-run budget cap and resume a run that failed on budget. Requires manage_workspace — that authorization is the approval. Body { approved, cap_usd?, reason? }; omit cap_usd to lift the cap. Writes a budget_override audit entry and an approval_override_granted governance event.
/api/workflows/{id}/eval-gateSet the server-side evaluation deploy gate { dataset_id?, min_pass_rate?, min_expected_match_rate?, max_avg_cost_usd? }. A subsequent POST /release returns 422 when the latest evaluation for that version regresses past the thresholds.
POST/PUT /api/budgets) now accept reservation_estimate_usd — a per-run hold that makes concurrent triggers admit atomically instead of racing past a limit — and their status carries committed_usd and a projected_usd forecast. Governance (PUT /api/governance) accepts redact_fields — named PII types (email/phone/credit_card/ssn/ip_address/api_key) scrubbed alongside your regexes. Block budgets are enforced on every trigger surface: manual, replay, webhook, schedule, and MCP.MCP server
POST /api/mcp is a streamable-HTTP MCP server (JSON-RPC 2.0, protocol 2025-06-18). It passes the normal auth middleware — required capability view to reach the endpoint — and then each tools/call enforces the capability of its REST equivalent, so a viewer credential can inspect but not run.
| Field | Type | Default | Description |
|---|---|---|---|
| initialize | method | — | Handshake; returns protocolVersion, capabilities.tools, and serverInfo. |
| tools/list | method | — | The six tools with their JSON input schemas. |
| tools/call | method | — | list_workflows · get_workflow · run_workflow · get_run · list_pending_approvals · approve_gate. |
| ping | method | — | Liveness check; returns an empty result. |
| notifications/* | notification | — | Accepted and acknowledged with 202 (e.g. notifications/initialized). |
Per-tool capabilities, structured results, and a Claude Code connection example live on the MCP page.
Auth
Signup, login, and accept-invite are auth-exempt — they are how you get a token. me and change-password require an authenticated session (or the owner, in open mode).
/api/auth/signupSelf-serve registration. Auth-exempt. Governed by SIGNUP_MODE: open (active immediately, returns a token), approval (pending — returns { pending: true }, no token), or closed (403).
{ "name": "Kim", "email": "kim@acme.com", "password": "correct horse battery" }
// → 201 (open mode)
{ "token": "eyJhbGciOi…", "member": { "id": "…", "role": "viewer", "status": "active", … }, "pending": false }
// → 201 (approval mode) — no token until an admin approves
{ "token": null, "member": { "…": "…", "status": "pending" }, "pending": true }
/api/auth/loginExchange email + password for a session token. Auth-exempt. 401 on bad credentials; 403 if the account is still awaiting admin approval.
{ "email": "dana@acme.com", "password": "…" }
// → 200
{ "token": "eyJhbGciOi…", "member": { "id": "77aa11bb22cc", "role": "editor", "status": "active", … } }
/api/auth/accept-inviteClaim an invite: { email, invite_code, name?, password }. Auth-exempt. Activates the member, sets their password, and returns a session token. 400 on a missing or mismatched code.
/api/auth/meThe current member plus the server's auth_mode (open | required). Requires an authenticated session; 401 otherwise.
{ "member": { "id": "77aa11bb22cc", "name": "Dana Lee", "role": "editor", "status": "active", … },
"auth_mode": "required" }
/api/auth/change-passwordSet a new password: { current_password?, new_password }. Requires an authenticated session. Returns 204. 400 if current_password is wrong for a member who already has one.
http://localhost:8000/docs — useful for exploring request schemas live. Real-time run event payloads are specified in Events & streaming.GET /api/auditand attributed on the member's stats page.