Server Architecture
NodeTool runs a single Fastify HTTP + WebSocket server (@nodetool-ai/websocket — packages/websocket/src/server.ts). The same process serves:
- REST routes under
/api/*(workflows, jobs, assets, models, settings, storage). - OpenAI-compatible
/v1/chat/completionsand/v1/models. - WebSocket endpoints for workflow execution, chat, the browser extension, downloads, and the agent runtime.
- Health and liveness probes.
Start it with nodetool serve (default 127.0.0.1:7777). serve accepts only --host and --port — there is no --mode flag.
Endpoint Matrix
For detailed schemas, see Chat API and Workflow API.
| Area | Path | Method / Protocol | Auth | Streaming | Notes |
|---|---|---|---|---|---|
| Models | /v1/models |
GET |
Bearer when AUTH_PROVIDER enforces |
no | OpenAI-compatible model listing |
| Chat | /v1/chat/completions |
POST |
Bearer when AUTH_PROVIDER enforces |
SSE when "stream": true |
OpenAI-compatible chat; SSE or single JSON |
| Workflows | /api/workflows |
GET |
Depends on AUTH_PROVIDER |
no | List workflows |
| Workflows | /api/workflows/{id}/run |
POST |
Depends on AUTH_PROVIDER |
no | Run a workflow once, return final outputs as one JSON response |
| Workflow WS | /ws |
WebSocket | Bearer header or api_key query when enforced |
yes | Workflow execution, chat, job control, live updates (MessagePack or JSON) |
| Agent WS | /ws/agent |
WebSocket | Bearer header or api_key query when enforced |
yes | Agent runtime |
| Extension WS | /ws/extension |
WebSocket | Follows global auth settings | yes | Browser extension channel |
| Download WS | /ws/download |
WebSocket | Follows global auth settings | yes | Model/file downloads |
| Storage | /api/storage/* |
HEAD/GET/PUT/DELETE |
Depends on AUTH_PROVIDER |
streaming for GET |
Asset/temp storage |
| Health | /health |
GET |
none | no | JSON: {status, timestamp, uptime, services} (200/503) |
| Health | /api/health |
GET |
none | no | JSON: {version, uptime} |
| Liveness | /ready |
GET |
none | no | Always 200 with {status:"ok"} |
When
AUTH_PROVIDERislocalornone, endpoints accept requests without a token for convenience. When it isstaticorsupabase, includeAuthorization: Bearer <token>on every request except the health/liveness routes.
Authentication and Headers
NodeTool uses Bearer token authentication. The behavior depends on your AUTH_PROVIDER setting:
| AUTH_PROVIDER | Token Required? | Use Case |
|---|---|---|
local / none |
No | Local development, desktop app |
static |
Yes — use the configured static token | Simple deployments with a shared secret |
supabase |
Yes — use a Supabase JWT | Production deployments with user management |
How to include credentials
- HTTP requests:
Authorization: Bearer <token>header on all non-public routes - WebSocket:
Authorization: Bearer <token>header (preferred) orapi_keyquery parameter - SSE streams (
/v1/chat/completions):Authorization: Bearer <token>andAccept: text/event-stream
Local development: When running locally with the default config (
AUTH_PROVIDER=local), no token is needed. You can omit theAuthorizationheader entirely.
See Authentication for full token handling rules.
Streaming Behavior
/v1/chat/completionsuses OpenAI-style SSE whenstreamis true; otherwise it returns a single JSON response.POST /api/workflows/{id}/rundoes not stream — it runs the workflow to completion and returns one JSON response.- The
/wsWebSocket streams workflow/job events (job_update,node_update,node_progress,output_update,chunk, …) and chat tokens/tool calls. See the WebSocket API for the full protocol. - Storage routes stream file contents for large assets.
Headless Mode: Running Workflows via CLI/API
NodeTool can run entirely without the UI—perfect for automation, CI/CD pipelines, and programmatic integrations. This section shows how to execute workflows from the command line or via HTTP requests.
Quick Start: Run a Workflow via cURL
# Run a workflow and get results (non-streaming)
curl -X POST "http://localhost:7777/api/workflows/YOUR_WORKFLOW_ID/run" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"params": {
"prompt": "A cyberpunk cityscape at sunset",
"style": "photorealistic"
}
}'
Response:
{
"job_id": "job_abc123",
"workflow_id": "YOUR_WORKFLOW_ID",
"status": "completed",
"outputs": {
"image": {
"type": "image",
"uri": "http://localhost:7777/api/storage/assets/abc123.png"
},
"caption": "Generated image of a cyberpunk cityscape..."
},
"error": null,
"message_count": 12,
"background": false
}
outputs is an object keyed by output-node name. The route does not stream — for
real-time progress, run the workflow over the WebSocket API.
Chat API (OpenAI-Compatible)
NodeTool exposes OpenAI-compatible endpoints, so you can use standard OpenAI clients:
# Simple chat completion
curl -X POST "http://localhost:7777/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Explain quantum computing in simple terms"}
]
}'
Response:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1699000000,
"model": "gpt-4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Quantum computing uses quantum mechanics..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 150,
"total_tokens": 160
}
}
Streaming Chat
# Streaming chat (prints tokens as they arrive)
curl -X POST "http://localhost:7777/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Write a haiku about programming"}
],
"stream": true
}'
Streaming response:
data: {"id":"chatcmpl-123","choices":[{"delta":{"role":"assistant"},"index":0}]}
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":"Code"},"index":0}]}
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":" flows"},"index":0}]}
data: {"id":"chatcmpl-123","choices":[{"delta":{"content":" like"},"index":0}]}
data: [DONE]
List Available Models
curl "http://localhost:7777/v1/models" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:
{
"object": "list",
"data": [
{"id": "gpt-4", "object": "model", "owned_by": "openai"},
{"id": "gpt-3.5-turbo", "object": "model", "owned_by": "openai"},
{"id": "claude-3-opus", "object": "model", "owned_by": "anthropic"},
{"id": "gpt-oss:20b", "object": "model", "owned_by": "ollama"}
]
}
List Workflows
# List all workflows
curl "http://localhost:7777/api/workflows" \
-H "Authorization: Bearer YOUR_TOKEN"
Health Check
# Check if server is running (no auth required)
curl "http://localhost:7777/health"
Response:
{
"status": "ok",
"timestamp": "2026-06-20T00:00:00.000Z",
"uptime": 123,
"services": { "database": "ok", "server": "ok" }
}
CLI Workflow Execution
You can also run workflows from the command line. nodetool run executes a
TypeScript/JavaScript DSL workflow file:
# Run a DSL workflow file
nodetool run ./my_workflow.ts
# Output results as JSON
nodetool run ./my_workflow.ts --json
To run a saved workflow by ID (requires a running server), use the workflows
subcommands:
# List workflows
nodetool workflows list
# Run a workflow by ID
nodetool workflows run workflow_abc123 --params '{"prompt": "test"}'
TypeScript / Node.js Client Example
const BASE_URL = 'http://localhost:7777';
const TOKEN = 'your_token_here';
// Run a workflow (runs to completion, returns one JSON response)
async function runWorkflow(workflowId, params) {
const response = await fetch(`${BASE_URL}/api/workflows/${workflowId}/run`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ params })
});
const body = await response.json();
// body: { job_id, workflow_id, status, outputs, error, message_count, background }
return body.outputs;
}
// For real-time progress, run the workflow over the WebSocket endpoint instead.
// See the WebSocket API page and examples/workflow_runner/js/workflow-runner.js.
// Using OpenAI SDK (works with NodeTool!)
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: TOKEN,
baseURL: `${BASE_URL}/v1`
});
const completion = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
});
console.log(completion.choices[0].message.content);
Python Client Example
import requests
BASE_URL = "http://localhost:7777"
TOKEN = "your_token_here" # Not needed for local development
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
# List workflows
workflows = requests.get(f"{BASE_URL}/api/workflows", headers=HEADERS).json()
# Run a workflow (runs to completion, returns one JSON response)
result = requests.post(
f"{BASE_URL}/api/workflows/{workflows[0]['id']}/run",
headers=HEADERS,
json={"params": {"prompt": "A sunset over mountains"}},
).json()
# result: {"job_id", "workflow_id", "status", "outputs", "error", "message_count", "background"}
print("Outputs:", result["outputs"])
# For real-time progress, run the workflow over the WebSocket endpoint instead.
# Use with OpenAI Python SDK (works with NodeTool!)
from openai import OpenAI
client = OpenAI(api_key=TOKEN, base_url=f"{BASE_URL}/v1")
completion = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}],
)
print(completion.choices[0].message.content)
Finding Your Workflow ID
To run a workflow via API, you need its ID. Here’s how to find it:
- From the UI: Open a workflow in the editor — the ID appears in the browser URL bar
- From the API: Call
GET /api/workflowsto list all workflows with their IDs - From the CLI: Run
nodetool workflows list
Error Handling
API errors return standard HTTP status codes with JSON error bodies:
{
"error": {
"message": "Workflow not found: invalid_id",
"type": "not_found",
"code": 404
}
}
| Status Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid parameters, malformed JSON |
| 401 | Unauthorized | Missing or invalid token |
| 403 | Forbidden | Token lacks permission |
| 404 | Not Found | Workflow/resource doesn’t exist |
| 422 | Validation Error | Parameter validation failed |
| 500 | Internal Error | Server-side error |
| 503 | Service Unavailable | Server overloaded or starting up |
Related Guides
- Chat API — OpenAI-compatible request/response schema and WebSocket usage.
- Workflow API — Workflow REST paths and execution.
- API Server Overview — Server architecture and modules.
- Deployment Guide — How servers are built and exposed.
- CLI Reference — Commands including
serve.