Import
Quick Start
ServerOptions
| Option | Type | Default | Description |
|---|---|---|---|
port | number | 7331 | TCP port to listen on |
db | BunSQLiteDatabase | undefined | Optional server-level SQLite database for mirroring run/event data and enabling the GET /v1/runs endpoint |
authToken | string | process.env.SMITHERS_API_KEY | Bearer token for authentication. If not set, falls back to the SMITHERS_API_KEY environment variable. If neither is set, authentication is disabled. |
maxBodyBytes | number | 1048576 (1MB) | Maximum request body size in bytes. Requests exceeding this limit receive a 413 response. |
rootDir | string | undefined | Root directory for workflow path resolution and tool sandboxing. Workflow paths must resolve within this directory. |
allowNetwork | boolean | false | Whether to allow network access in the bash tool for all runs started by this server |
Return Value
startServer returns a Node.js http.Server instance. The server is already listening when returned.
Authentication
IfauthToken is configured (or SMITHERS_API_KEY is set), every request must include a valid token via one of:
- Authorization header:
Authorization: Bearer <token> - Custom header:
x-smithers-key: <token>
401 response:
API Routes
All routes use JSON request/response bodies (except the SSE endpoint). Responses includeContent-Type: application/json, Cache-Control: no-store, and X-Content-Type-Options: nosniff headers.
POST /v1/runs
Start a new workflow run, or resume an existing one. Request body:- The workflow file is dynamically imported from
workflowPath(must export default). - The workflow’s SQLite tables are automatically created if needed.
- The run is started asynchronously. The response is returned immediately with the
runId. - If
resume: true, therunIdis required and the run must already exist in the database. - If a run with the same
runIdis already in progress andresumeis not set, a409error is returned.
| Status | Code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | Missing or invalid workflowPath, input, or config |
| 400 | RUN_ID_REQUIRED | resume: true but no runId provided |
| 400 | WORKFLOW_PATH_OUTSIDE_ROOT | Workflow path resolves outside rootDir |
| 404 | RUN_NOT_FOUND | resume: true but the run does not exist |
| 409 | RUN_IN_PROGRESS | A run with this runId is already active |
| 409 | RUN_ALREADY_EXISTS | A run with this runId already exists (and resume is false) |
POST /v1/runs/:runId/resume
Resume a previously paused or failed workflow run. Request body:- The run must already exist in the database.
- If the run is currently active, returns
200with the current status without starting a new process. - Otherwise, reloads the workflow and resumes execution from the last checkpoint.
| Status | Code | Condition |
|---|---|---|
| 400 | INVALID_REQUEST | Missing or invalid workflowPath |
| 404 | RUN_NOT_FOUND | Run does not exist |
POST /v1/runs/:runId/cancel
Cancel a running workflow. Response (200):- Signals the run’s
AbortControllerto cancel execution. - If the run is not found in the active run map, returns a
404.
| Status | Code | Condition |
|---|---|---|
| 404 | NOT_FOUND | Run not found in active runs |
GET /v1/runs/:runId
Get the current status and summary of a run. Response (200):| Field | Type | Description |
|---|---|---|
runId | string | The run identifier |
workflowName | string | Name derived from the workflow file |
status | string | Current status: running, waiting-approval, finished, failed, cancelled |
startedAtMs | number | null | Start timestamp in milliseconds |
finishedAtMs | number | null | Finish timestamp in milliseconds |
summary | object | Node count by state (e.g., { finished: 3, pending: 2 }) |
GET /v1/runs/:runId/events
Stream lifecycle events via Server-Sent Events (SSE). Query parameters:| Parameter | Type | Default | Description |
|---|---|---|---|
afterSeq | number | -1 | Only return events after this sequence number |
text/event-stream
- Events are named
smitherswith JSON payloads matching theSmithersEventunion type. - The server polls the database every 500ms for new events.
- A
: keep-alivecomment is sent every 10 seconds when no events are available. - The stream closes automatically when the run reaches a terminal state (
finished,failed,cancelled) and all events have been delivered. - The client can reconnect using
afterSeqto resume from where it left off.
GET /v1/runs/:runId/frames
List render frames for a run. Query parameters:| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Maximum number of frames to return |
afterFrameNo | number | undefined | Only return frames after this frame number |
POST /v1/runs/:runId/nodes/:nodeId/approve
Approve a node that is waiting for human approval. Request body:POST /v1/runs/:runId/nodes/:nodeId/deny
Deny a node that is waiting for human approval. Request body:GET /v1/runs
List all runs. Requires a server-leveldb to be configured.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Maximum number of runs to return |
status | string | undefined | Filter by status (e.g., running, finished) |
| Status | Code | Condition |
|---|---|---|
| 400 | DB_NOT_CONFIGURED | No server-level database was provided to startServer |
Error Response Format
All error responses follow a consistent format:500 with code SERVER_ERROR.
Database Mirroring
When a server-leveldb is provided and the workflow uses a different database, the server automatically mirrors run metadata and events to the server database. This enables the GET /v1/runs endpoint to list runs across all workflows.
Events are mirrored asynchronously and do not block the workflow execution.
Example: Full Server Setup
curl: