Get started with Agent Orcha in minutes using Docker (recommended). Follow these steps to set up your first multi-agent system.
Agent Orcha includes a built-in web dashboard accessible at http://localhost:3000 when the server is running. The Studio provides a visual interface for managing and testing your entire instance. When AUTH_PASSWORD is set, the Studio will prompt for the password before granting access.
| Tab | Description |
|---|---|
| Agents | Browse all agents, invoke with custom input, stream responses, manage conversation sessions |
| Knowledge | Browse and search knowledge stores, view entities and graph structure for stores with direct mapping |
| MCP | Browse MCP servers, view available tools per server, call tools directly |
| Workflows | Browse and execute workflows (step-based and ReAct), stream execution progress, respond to Human-in-the-Loop prompts |
| Skills | Browse and inspect available skills attached to agents |
| Monitor | View LLM call logs with context size, token estimates, and duration metrics |
| IDE | Full in-browser file editor with file tree, syntax highlighting (YAML, JSON, JS), hot-reload on save |
Docker is the recommended way to run Agent Orcha. The ddalcu/agent-orcha image includes everything you need — no Node.js installation required.
docker run -v ./my-agent-orcha-project:/data ddalcu/agent-orcha init
docker run -p 3000:3000 -e AUTH_PASSWORD=mypass -v ./my-agent-orcha-project:/data ddalcu/agent-orcha start
For a more manageable setup, use a docker-compose.yaml file:
services:
agent-orcha:
image: ddalcu/agent-orcha
ports:
- "3000:3000"
volumes:
- ./my-agent-orcha-project:/data
environment:
AUTH_PASSWORD: mypass
| Variable | Description | Default |
|---|---|---|
WORKSPACE |
Base directory for the project inside the container | /data |
PORT |
Port for the server to listen on | 3000 |
AUTH_PASSWORD |
When set, all API routes and the Studio UI require this password to access. When unset, no authentication is required. | unset (no auth) |
CORS_ORIGIN |
Controls cross-origin request policy. When unset, only same-origin requests are allowed. Set to * to allow all origins, or a specific URL (e.g. https://your-frontend.com) to allow that origin. |
unset (same-origin only) |
BROWSER_SANDBOX |
When true, starts Chromium + Xvfb + VNC inside the container for browser sandbox tools. The VNC viewer is accessible through the Studio UI at /vnc. |
true |
If you prefer not to use Docker, you can run Agent Orcha directly via npx (requires Node.js 24+).
| Command | Description | Options |
|---|---|---|
npx agent-orcha init [dir] |
Initialize a new project with example configs | dir - Target directory (default: current) |
npx agent-orcha start |
Start the agent orchestrator server | Environment variables: PORT, HOST, WORKSPACE |
npx agent-orcha help |
Show help information | - |
The diagram below shows how the major components fit together — from input sources through the orchestrator to tool execution.
Agents are AI-powered units that can use tools and respond to queries. Each agent is defined in a YAML file with its configuration.
name: string # Unique identifier (required)
description: string # Human-readable description (required)
version: string # Semantic version (default: "1.0.0")
llm: string | object # Reference to LLM config in llm.json
# Simple: llm: default
# With override: llm: { name: default, temperature: 0.3 }
prompt: # Prompt configuration (required)
system: string # System message/instructions
inputVariables: [string] # Variables to interpolate
tools: # Tools available to agent (optional)
- mcp:<server-name> # MCP server tools
- knowledge:<store-name> # Knowledge store search
- function:<function-name> # Custom functions
- builtin:<tool-name> # Built-in tools (e.g., ask_user)
- sandbox:<tool-name> # Sandbox tools (exec, shell, browser, web)
skills: # Skills to inject into system prompt (optional)
- skill-name # Array of skill names
# Or: skills: { mode: all } # Attach all available skills
memory: boolean | object # Persistent memory across sessions (optional)
# Simple: memory: true
# With options: memory: { enabled: true, maxLines: 100 }
output: # Output formatting (optional)
format: text | structured
schema: # Required when format is "structured"
type: object
properties: { ... }
required: [string]
triggers: # Automatic execution triggers (optional)
- type: cron
schedule: "0 9 * * *" # Cron expression
input: { key: value } # Input for the agent
- type: webhook
path: /api/triggers/webhooks/my-hook
input: { key: value } # Base input (merged with request body)
integrations: # External connectors (optional)
- type: collabnook # Chat platform connector
url: https://collabnook.example.com
channel: general
botName: orcha-bot
- type: email # Email connector (IMAP + SMTP)
imap: { host: imap.gmail.com, port: 993, secure: true }
smtp: { host: smtp.gmail.com, port: 587, secure: false }
auth: { user: agent@example.com, pass: app-password }
publish: boolean | object # Standalone chat page (optional)
# Simple: publish: true
# With password: publish: { enabled: true, password: "secret" }
name: researcher
description: Researches topics using knowledge search and web fetch
version: "1.0.0"
llm:
name: default
temperature: 0.5
prompt:
system: |
You are a thorough researcher. Use available tools
to gather information before responding.
inputVariables:
- topic
tools:
- mcp:fetch
- knowledge:docs
- function:fibonacci
output:
format: text
Agents can return validated, structured JSON output by specifying an output.schema. This ensures responses match your schema with automatic validation.
name: sentiment-structured
description: Sentiment analysis with structured output
llm:
name: default
temperature: 0
prompt:
system: |
Analyze the sentiment of the provided text.
inputVariables:
- text
output:
format: structured
schema:
type: object
properties:
sentiment:
type: string
enum: [positive, negative, neutral]
confidence:
type: number
minimum: 0
maximum: 1
keywords:
type: array
items:
type: string
required:
- sentiment
- confidence
Agents support session-based conversation memory for multi-turn dialogues. Pass a sessionId in API calls to maintain context across interactions.
# First message with sessionId
curl -X POST http://localhost:3000/api/agents/chatbot/invoke \
-H "Content-Type: application/json" \
-d '{"input": {"message": "My name is Alice"}, "sessionId": "user-123"}'
# Second message - agent remembers context
curl -X POST http://localhost:3000/api/agents/chatbot/invoke \
-H "Content-Type: application/json" \
-d '{"input": {"message": "What is my name?"}, "sessionId": "user-123"}'
Workflows orchestrate multiple agents. Agent Orcha supports two workflow types: step-based (sequential/parallel with explicit steps) and ReAct (autonomous, prompt-driven).
name: string # Unique identifier (required)
description: string # Human-readable description (required)
version: string # Semantic version (default: "1.0.0")
type: steps # Optional (steps is default)
input: # Input schema (required)
schema:
<field_name>:
type: string | number | boolean | array | object
required: boolean
default: any
description: string
steps: # Workflow steps (required)
- id: string # Unique step identifier
agent: string # Agent name to execute
input: # Input mapping using templates
<key>: "{{input.field}}"
<key>: "{{steps.stepId.output}}"
condition: string # Optional conditional execution
retry: # Optional retry configuration
maxAttempts: number
delay: number
output:
key: string
# Parallel execution block
- parallel:
- id: step-a
agent: agent-a
input: { ... }
config: # Workflow configuration (optional)
timeout: number # Total timeout ms (default: 300000)
onError: stop | continue | retry
output: # Output mapping (required)
<key>: "{{steps.stepId.output}}"
Access data within workflows using double curly braces:
| Template | Description |
|---|---|
{{input.fieldName}} |
Access workflow input field |
{{steps.stepId.output}} |
Access step output |
{{steps.stepId.output.nested.path}} |
Access nested output |
{{steps.stepId.metadata.duration}} |
Access step metadata |
ReAct workflows use autonomous agents that discover and call tools/agents based on your prompt, without explicit step definitions.
name: string
type: react # Required for ReAct
input:
schema:
<field>:
type: string
required: true
prompt:
system: string # System instructions
goal: string # Goal template (supports {{input.*}})
graph:
model: string # LLM config name from llm.json
executionMode: react | single-turn # Default: react
tools:
mode: all | include | exclude | none
sources: [mcp, knowledge, function, builtin]
include: [string] # For mode: include
exclude: [string] # For mode: exclude
agents:
mode: all | include | exclude | none
maxIterations: number # Default: 10
timeout: number # Default: 300000
output:
<key>: "{{state.messages[-1].content}}"
| Mode | Behavior | Best For |
|---|---|---|
single-turn |
Calls tools once, then returns | Research, data gathering, straightforward tasks |
react |
Multiple rounds of tool calls with analysis | Complex problems, iterative refinement |
ReAct workflows support the builtin:ask_user tool. When called, the workflow pauses and waits for user input. In the Studio Workflows tab, an inline input prompt appears automatically — type your response and the workflow resumes. You can also respond via the Monitor tab or the Tasks API (POST /api/tasks/:id/respond).
Functions are custom JavaScript tools that extend agent capabilities. They're simple to create and require no dependencies.
export default {
name: 'function-name',
description: 'What it does',
parameters: {
param1: {
type: 'number', // string | number | boolean | array | object | enum
description: 'Parameter description',
required: true,
default: 0,
},
},
execute: async ({ param1 }) => {
// Your logic here
return `Result: ${param1}`;
},
};
export const metadata = {
name: 'function-name',
version: '1.0.0',
author: 'Your Name',
tags: ['category'],
};
export default {
name: 'fibonacci',
description: 'Returns the nth Fibonacci number (0-based indexing)',
parameters: {
n: {
type: 'number',
description: 'The index (0-based, max 100)',
},
},
execute: async ({ n }) => {
if (n < 0 || !Number.isInteger(n)) {
throw new Error('Index must be a non-negative integer');
}
if (n > 100) {
throw new Error('Index too large (max 100)');
}
if (n === 0) return 'Fibonacci(0) = 0';
if (n === 1) return 'Fibonacci(1) = 1';
let prev = 0, curr = 1;
for (let i = 2; i <= n; i++) {
[prev, curr] = [curr, prev + curr];
}
return `Fibonacci(${n}) = ${curr}`;
},
};
Model Context Protocol (MCP) servers provide external tools to agents. Configure them in mcp.json.
{
"version": "1.0.0",
"servers": {
"fetch": {
"transport": "streamable-http",
"url": "https://remote.mcpservers.org/fetch/mcp",
"description": "Web fetch capabilities",
"timeout": 30000,
"enabled": true
},
"filesystem": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
},
"globalOptions": {
"throwOnLoadError": false,
"prefixToolNameWithServerName": true,
"defaultToolTimeout": 30000
}
}
Skills are prompt augmentation units that expand what an agent knows how to do. Unlike tools or functions, skills are not executable code — they are knowledge and instruction bundles (Markdown files) that get injected into an agent's system prompt at runtime.
Each skill lives in its own directory under skills/:
skills/
orcha-builder/
SKILL.md
pii-guard/
SKILL.md
sandbox/
SKILL.md
web-pilot/
SKILL.md
A SKILL.md file has optional YAML frontmatter followed by Markdown content:
---
name: pii-guard
description: PII and OWASP security filtering rules
---
# PII Guard
## Sensitive Data Categories (NEVER include in responses)
- SSN, tax IDs, government identifiers
- Salary, compensation, financial data
- Home addresses, personal phone numbers
- Dates of birth, ages
- Full email addresses (mask as j***@company.com)
The frontmatter fields:
Attach skills to agents in their YAML definition file:
# Specific skills by name
name: corporate
description: Friendly corporate assistant
llm: default
prompt:
system: You are a friendly corporate assistant.
inputVariables:
- query
skills:
- pii-guard
# Or attach all available skills
skills:
mode: all
When an agent with skills is invoked, the skill content is appended to the agent's system prompt before the LLM call. Each skill is wrapped in <skill name="..."> delimiters so the LLM can distinguish between different skill instructions.
skills/ with your skill name.SKILL.md file with frontmatter and instructions.skills array.Community-contributed skills that you can copy into your project:
| Name | Description | Author |
|---|---|---|
| orcha-builder | Documentation for creating and modifying ORCHA resources | Agent Orcha |
| pii-guard | PII and OWASP security filtering rules | Agent Orcha |
| sandbox | JavaScript sandbox, shell, web fetch, and browser tools | Agent Orcha |
| web-pilot | Browser automation via observe-act loop with element refs | Agent Orcha |
Want to contribute a skill? Add it to templates/skills/ and submit a pull request to the repository.
Tasks provide async execution for agents and workflows. Submit a task, poll its status, stream updates via SSE, or cancel it. Tasks are managed in-memory with automatic cleanup.
Tasks progress through these statuses:
submitted → working → completed | failed | canceled
ReAct workflows may also reach input-required when the builtin:ask_user tool is called, pausing until you respond via the API.
# Submit an agent task
curl -X POST http://localhost:3000/api/tasks/agent \
-H "Content-Type: application/json" \
-d '{
"agent": "researcher",
"input": {"topic": "AI trends"},
"sessionId": "optional-session-id"
}'
# Submit a workflow task
curl -X POST http://localhost:3000/api/tasks/workflow \
-H "Content-Type: application/json" \
-d '{
"workflow": "research-paper",
"input": {"topic": "AI trends"}
}'
curl -N http://localhost:3000/api/tasks/TASK_ID/stream
When a ReAct workflow reaches input-required, respond to continue execution:
curl -X POST http://localhost:3000/api/tasks/TASK_ID/respond \
-H "Content-Type: application/json" \
-d '{"response": "Yes, proceed with option A"}'
The sandbox system provides isolated execution environments for agents. It includes tools for JavaScript execution, shell commands, web interaction, and browser automation.
Configure sandbox behavior in sandbox.json (optional — defaults to enabled):
{
"enabled": true,
"commandTimeout": 30000,
"maxOutputChars": 50000,
"browserCdpUrl": "http://localhost:9222"
}
| Tool | Description | Parameters |
|---|---|---|
sandbox:exec |
Execute JavaScript code in a sandboxed VM | code (string), timeout (number, optional) |
sandbox:shell |
Execute shell commands as a non-root sandbox user |
command (string), timeout (number, optional) |
sandbox:web_fetch |
Fetch a web page and convert HTML to Markdown | url (string), raw (boolean, optional) |
sandbox:web_search |
Search the web via DuckDuckGo | query (string), num_results (number, optional) |
sandbox:browser_navigate |
Navigate the browser to a URL and wait for page load | url (string) |
sandbox:browser_observe |
Get a text snapshot of the current page (accessibility tree) | — |
sandbox:browser_click |
Click an element by CSS selector | selector (string) |
sandbox:browser_type |
Type text into a focused element | selector (string), text (string) |
sandbox:browser_screenshot |
Take a screenshot (returns image to the LLM as a multimodal content part) | — |
sandbox:browser_evaluate |
Execute JavaScript in the browser page context | expression (string) |
Browser tools require Chromium running inside the Docker container. Set BROWSER_SANDBOX=true (enabled by default in the Docker image) to start Xvfb + Chromium + VNC automatically. The VNC viewer is accessible through the Studio UI at /vnc (protected by AUTH_PASSWORD when set).
name: code-runner
description: Agent that can execute code and search the web
llm: default
prompt:
system: |
You can execute JavaScript code and search the web.
inputVariables:
- task
tools:
- sandbox:exec
- sandbox:shell
- sandbox:web_fetch
- sandbox:web_search
- sandbox:browser_navigate
- sandbox:browser_observe
- sandbox:browser_click
- sandbox:browser_type
- sandbox:browser_screenshot
- sandbox:browser_evaluate
Agent Orcha has two memory layers. Conversation memory (session-based, in-memory) tracks multi-turn dialogues. Persistent memory saves facts to disk so agents remember across sessions and server restarts.
name: assistant
description: Agent with persistent memory
llm: default
prompt:
system: |
You are a helpful assistant. Save important facts
about the user to your memory for future sessions.
inputVariables:
- message
memory: true # Enable persistent memory
memory: true, the agent gets a save_memory built-in tool.memory/{agentName}.mdsave_memoryTriggers allow agents to run automatically on a schedule (cron) or in response to external events (webhook).
name: daily-reporter
description: Runs every morning at 9am
llm: default
prompt:
system: Generate a daily summary report.
inputVariables:
- task
triggers:
- type: cron
schedule: "0 9 * * *"
input:
task: "Generate the daily report"
name: webhook-handler
description: Responds to external webhook events
llm: default
prompt:
system: Process the incoming webhook payload.
inputVariables:
- data
triggers:
- type: webhook
path: /api/triggers/webhooks/webhook-handler
input:
data: "default context"
Webhook triggers register a POST endpoint at the specified path. The request body is merged with the base input and the agent is invoked immediately.
Integrations connect agents to external communication platforms. When configured, agents can receive messages from external sources and respond back through the same channel. Agent Orcha currently supports two integration types: Collabnook (real-time chat) and Email (IMAP/SMTP).
Integrations are added to agents via the integrations field in the agent YAML. Each integration type injects its own input variables and tools into the agent automatically.
Collabnook is a minimalistic anonymous chat with audio and video calls — no sign-up required. The Collabnook integration connects to it via WebSocket. When active, the agent joins a channel and responds to @mentions in real time.
channelContext, channelMembers)| Field | Type | Description |
|---|---|---|
type |
string | "collabnook" (required) |
url |
string | Collabnook server URL (required) |
channel |
string | Channel to join (required) |
botName |
string | Display name for the bot in the channel (required) |
password |
string | Server password if authentication is required (optional) |
replyDelay |
number | Delay in ms before posting the reply (optional) |
name: channel-bot
description: Bot connected to a chat channel
llm: default
prompt:
system: |
You are a channel assistant.
Use the channel context to respond to messages.
inputVariables:
- message
- channelContext
- channelMembers
integrations:
- type: collabnook
url: https://collabnook.example.com
channel: general
botName: orcha-bot
| Variable | Description |
|---|---|
channelContext |
Recent channel messages formatted as text |
channelMembers |
List of channel members with userId and name |
Connect agents to email via IMAP polling (inbound) and SMTP (outbound). The agent polls a mailbox folder for new emails and is automatically invoked when one arrives. The agent's response is sent back to the original sender as a reply. Agents also receive an email_send tool for composing new outbound emails to any address.
email_send built-in tool for composing new emails to arbitrary recipients| Field | Type | Description |
|---|---|---|
type |
string | "email" (required) |
imap.host |
string | IMAP server hostname (required) |
imap.port |
number | IMAP port (default: 993) |
imap.secure |
boolean | Use TLS for IMAP (default: true) |
smtp.host |
string | SMTP server hostname (required) |
smtp.port |
number | SMTP port (default: 587) |
smtp.secure |
boolean | Use TLS for SMTP (default: false) |
auth.user |
string | Email account username (optional) |
auth.pass |
string | Email account password or app password (optional) |
fromName |
string | Display name for outbound emails (optional) |
fromAddress |
string | From address override — defaults to auth.user (optional) |
pollInterval |
number | Seconds between mailbox checks (default: 60) |
folder |
string | IMAP folder to monitor (default: INBOX) |
name: support-agent
description: Handles inbound support emails
llm: default
prompt:
system: |
You are a customer support agent.
Reply helpfully to inbound emails.
Use the email_send tool if you need to
contact other departments.
inputVariables:
- message
integrations:
- type: email
imap:
host: imap.gmail.com
port: 993
secure: true
smtp:
host: smtp.gmail.com
port: 587
secure: false
auth:
user: support@example.com
pass: app-password
fromName: "Support Agent"
pollInterval: 30
folder: INBOX
| Tool | Description | Parameters |
|---|---|---|
email_send |
Compose and send a new email | to (string), subject (string), body (string) |
Published agents get standalone chat pages accessible at /chat/<agent-name>, independent of the Studio UI. This is useful for sharing agents with end-users who don't need access to the full Studio interface.
Add the publish field to any agent YAML file:
# Simple — publish with no password
name: my-chatbot
description: A helpful assistant
llm: default
prompt:
system: You are a helpful assistant.
inputVariables:
- message
publish: true
# With password protection
publish:
enabled: true
password: "secret123"
/chat/<agent-name> to open the standalone chat pageAUTH_PASSWORDsessionStorageKnowledge stores are one of the most powerful features of Agent Orcha and a key differentiator from other agentic frameworks. They combine semantic vector search, knowledge graphs, and direct SQL querying in a single unified system — all from a simple YAML definition.
Most frameworks stop at basic RAG (embed chunks, search by similarity). Agent Orcha goes further:
directMapping. Deterministic extraction from columns — no LLM calls, 100% data preservation.All knowledge stores share a single unified schema. A store without graph does vector-only search; adding graph.directMapping enables entity extraction and graph traversal.
name: string # Unique identifier (required)
description: string # Human-readable description (required)
source: # Data source (required)
type: directory | file | database | web
# Web-specific options:
selector: string # CSS selector for html loader (optional)
headers: { key: value } # Custom/auth headers (optional)
jsonPath: string # Dot-notation path to extract nested array from JSON (optional)
loader: # Document loader (optional)
type: text | pdf | csv | json | markdown | html
# Defaults: html for web sources, text for file/directory
# Not used for database sources (has its own row-to-document logic)
splitter: # Text chunking (required)
type: character | recursive | token | markdown
chunkSize: number # Characters per chunk (default: 1000)
chunkOverlap: number # Overlap between chunks (default: 200)
embedding: string # Reference to embedding config in llm.json (default: "default")
graph: # Optional - enables entity graph
directMapping: # Maps structured data to entities and relationships
entities:
- type: string # Entity type name
idColumn: string # Column used as unique ID
nameColumn: string # Column used as display name (optional)
properties: [string] # Columns to include as entity properties
relationships: # Optional
- type: string # Relationship type name
source: string # Source entity type
target: string # Target entity type
sourceIdColumn: string # Column linking to source entity
targetIdColumn: string # Column linking to target entity
search: # Search configuration (optional)
defaultK: number # Results per search (default: 4)
scoreThreshold: number # Minimum similarity score (0-1)
reindex: # Periodic re-indexing (optional)
schedule: string # Cron expression (e.g., "0 * * * *" for hourly)
name: docs
description: Documentation for semantic search
source:
type: directory
path: knowledge/sample-data
pattern: "*.txt"
loader:
type: text
splitter:
type: character
chunkSize: 1000
chunkOverlap: 200
embedding: default
search:
defaultK: 4
scoreThreshold: 0.2
Add a graph.directMapping section to build a knowledge graph from structured data (database, CSV, or JSON sources). Entities and relationships are mapped deterministically from columns/keys — no LLM calls during extraction, 100% data preservation.
name: blog-posts
description: Blog posts with authors as a knowledge graph
source:
type: database
connectionString: postgresql://user:pass@localhost:5432/blog
query: |
SELECT p.id, p.title, p.slug, p.html AS content,
u.name AS author_name, u.email AS author_email
FROM posts p
LEFT JOIN users u ON p.author_id = u.id
WHERE p.status = 'published'
contentColumn: content
metadataColumns: [id, title, slug, author_name, author_email]
splitter:
type: recursive
chunkSize: 2000
chunkOverlap: 300
embedding: default
graph:
directMapping:
entities:
- type: Post
idColumn: id
nameColumn: title
properties: [title, slug, content]
- type: Author
idColumn: author_email
nameColumn: author_name
properties: [author_name, author_email]
relationships:
- type: WROTE
source: Author
target: Post
sourceIdColumn: author_email
targetIdColumn: id
search:
defaultK: 10
Each entity definition maps a row's columns to a graph node. The idColumn determines uniqueness (duplicate IDs are merged), nameColumn sets the display name, and properties lists which columns to attach as node properties. Relationships connect entities via their ID columns.
You can also build knowledge graphs from JSON API endpoints. When a JSON source contains an array of objects, each object is treated as a row with keys mapped to columns — just like database and CSV sources.
name: team-directory
description: Team members from internal API
source:
type: web
url: https://api.internal.com/v1/employees
jsonPath: data.employees
headers:
Authorization: "Bearer ${EMPLOYEE_API_TOKEN}"
loader:
type: json
splitter:
type: character
chunkSize: 500
embedding: default
graph:
directMapping:
entities:
- type: Employee
idColumn: id
nameColumn: name
properties: [name, email, role]
- type: Department
idColumn: department
nameColumn: department
properties: [department]
relationships:
- type: BELONGS_TO
source: Employee
target: Department
sourceIdColumn: id
targetIdColumn: department
.knowledge-data/{name}.dbgraph.directMapping also store entities and relationships in SQLite, with vector embeddings for entity similarity searchAll knowledge stores support the same source types for loading documents.
| Type | Description | Key Options |
|---|---|---|
directory |
Load files from a local directory | path, pattern (glob), recursive |
file |
Load a single file | path |
database |
Query PostgreSQL, MySQL, or SQLite. Agents also get a readonly SQL tool for direct querying. | connectionString, query, contentColumn, metadataColumns, batchSize |
web |
Fetch content from a URL (HTML, JSON, text, CSV) | url, selector (CSS, html only), headers (auth/custom), jsonPath (dot-notation for nested arrays) |
Paths for directory and file sources are relative to the project root.
The loader.type determines how fetched content is parsed. It is optional — defaults to html for web sources and text for file/directory sources. Not used for database sources.
| Type | Description | Graph Support |
|---|---|---|
html |
Parse HTML with Cheerio, optional CSS selector. Default for web sources. |
No |
text / markdown |
Raw text as a single document. Default for file/directory sources. | No |
json |
Array of objects: each object becomes a row with _rawRow metadata. Other JSON: extracts all string values. |
Yes (array of objects) |
csv |
One document per row, formatted as "column: value" pairs with _rawRow metadata. |
Yes |
pdf |
Extract text from PDF files (requires pdf-parse package). File/directory only. |
No |
All stores use the same unified search pipeline:
search:
defaultK: 4 # Results per search (default: 4)
scoreThreshold: 0.2 # Minimum similarity score (optional)
Knowledge stores can be configured to automatically re-index on a cron schedule. The cron checks source hashes and only re-indexes when the underlying data has changed.
reindex:
schedule: "0 * * * *" # Cron expression (e.g., every hour)
All data is persisted to SQLite at .knowledge-data/{name}.db. On server restart, source file hashes are compared — if nothing changed, the store is restored instantly from SQLite without re-embedding or re-extracting. To force a full rebuild, use the Re-index button in the Studio or call POST /api/knowledge/:name/index.
For knowledge stores backed by a database source, agents automatically receive a readonly SQL tool (knowledge_sql_{name}) in addition to the semantic search tool. This lets agents query the source database directly using SQL — enabling precise filtering, aggregation, joins, and counts that vector search alone cannot provide.
source.query — the agent sees the available tables and columnsSELECT queries are allowed — INSERT, UPDATE, DELETE, and DDL statements are blocked| Use Case | Best Tool |
|---|---|
| "Find documents about customer satisfaction" | Semantic search — meaning-based matching |
| "How many orders were placed last month?" | SQL — precise aggregation |
| "Show me all calls handled by agent John" | SQL — exact filtering |
| "What topics come up most in support tickets?" | Both — SQL for counts, semantic for themes |
The agent decides which tool to use based on the question. Having both available means agents can combine the precision of SQL with the flexibility of semantic search in a single conversation.
name: music-store
description: Music catalog with artists, albums, and tracks
source:
type: database
connectionString: sqlite://knowledge/music-store/musicstore.sqlite
query: |
SELECT t.TrackId, t.Name AS track, a.Title AS album,
ar.Name AS artist, g.Name AS genre, t.UnitPrice
FROM tracks t
JOIN albums a ON t.AlbumId = a.AlbumId
JOIN artists ar ON a.ArtistId = ar.ArtistId
JOIN genres g ON t.GenreId = g.GenreId
contentColumn: track
metadataColumns: [TrackId, album, artist, genre, UnitPrice]
splitter:
type: character
chunkSize: 500
embedding: default
graph:
directMapping:
entities:
- type: Artist
idColumn: artist
nameColumn: artist
properties: [artist]
- type: Genre
idColumn: genre
nameColumn: genre
properties: [genre]
relationships:
- type: PLAYS
source: Artist
target: Genre
sourceIdColumn: artist
targetIdColumn: genre
With this configuration, an agent can semantically search for "upbeat jazz tracks", run SELECT artist, COUNT(*) FROM tracks GROUP BY artist ORDER BY COUNT(*) DESC LIMIT 5 to find the most prolific artists, and traverse the knowledge graph to discover genre relationships — all from the same knowledge store.
Complete REST API documentation for interacting with agents, workflows, knowledge stores, functions, MCP servers, files, and LLMs.
GET /health
Response:
{
"status": "ok",
"timestamp": "2026-01-21T12:00:00.000Z"
}
When the AUTH_PASSWORD environment variable is set, all /api/* routes require a valid session cookie. The /health endpoint is always public. When AUTH_PASSWORD is not set, authentication is disabled and all routes are open.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/auth/check |
Check authentication status (never returns 401) |
| POST | /api/auth/login |
Authenticate with password, returns session cookie |
| POST | /api/auth/logout |
Invalidate session and clear cookie |
POST /api/auth/login
Content-Type: application/json
Request:
{
"password": "your-secret-password"
}
Response (success — sets HttpOnly session cookie):
{
"authenticated": true
}
Response (wrong password):
HTTP 401
{
"error": "Invalid password"
}
GET /api/auth/check
Response (auth enabled, not logged in):
{
"authenticated": false,
"required": true
}
Response (auth disabled):
{
"authenticated": true,
"required": false
}
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/agents |
List all agents |
| GET | /api/agents/:name |
Get agent details |
| POST | /api/agents/:name/invoke |
Run agent |
| POST | /api/agents/:name/stream |
Stream agent response (SSE) |
| GET | /api/agents/sessions/stats |
Get session statistics |
| GET | /api/agents/sessions/:sessionId |
Get session details |
| DELETE | /api/agents/sessions/:sessionId |
Clear session messages |
curl -X POST http://localhost:3000/api/agents/researcher/invoke \
-H "Content-Type: application/json" \
-d '{
"input": {
"topic": "your topic"
},
"sessionId": "optional-session-id"
}'
{
"output": "Agent response text",
"metadata": {
"tokensUsed": 150,
"toolCalls": [],
"duration": 1234,
"sessionId": "optional-session-id",
"messagesInSession": 4,
"structuredOutputValid": true
}
}
These endpoints power the standalone chat pages for published agents. They are exempt from global AUTH_PASSWORD authentication and handle their own per-agent auth.
| Method | Endpoint | Description |
|---|---|---|
| GET | /chat/:agentName |
Serve standalone chat page (HTML) |
| GET | /api/chat/:agentName/config |
Get published agent config (name, description, requiresPassword) |
| POST | /api/chat/:agentName/auth |
Authenticate with agent password, returns token |
| POST | /api/chat/:agentName/stream |
Stream agent response (SSE), requires X-Chat-Token header if password-protected |
curl -X POST http://localhost:3000/api/chat/my-agent/auth \
-H "Content-Type: application/json" \
-d '{ "password": "secret123" }'
# Response: { "token": "abc123..." }
curl -N -X POST http://localhost:3000/api/chat/my-agent/stream \
-H "Content-Type: application/json" \
-H "X-Chat-Token: abc123..." \
-d '{
"input": { "message": "Hello!" },
"sessionId": "optional-session-id"
}'
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/workflows |
List all workflows |
| GET | /api/workflows/:name |
Get workflow details |
| POST | /api/workflows/:name/run |
Execute workflow |
| POST | /api/workflows/:name/stream |
Stream workflow execution (SSE) |
curl -X POST http://localhost:3000/api/workflows/research-paper/run \
-H "Content-Type: application/json" \
-d '{
"input": {
"topic": "research topic",
"style": "professional"
}
}'
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/knowledge |
List all knowledge stores |
| GET | /api/knowledge/:name |
Get knowledge store config |
| POST | /api/knowledge/:name/search |
Search knowledge store |
| POST | /api/knowledge/:name/refresh |
Reload documents |
| POST | /api/knowledge/:name/add |
Add documents |
| GET | /api/knowledge/:name/entities |
Get graph entities |
| GET | /api/knowledge/:name/edges |
Get graph edges |
curl -X POST http://localhost:3000/api/knowledge/docs/search \
-H "Content-Type: application/json" \
-d '{
"query": "search term",
"k": 4
}'
Dynamically add documents to a knowledge store without restarting the server. If the store isn't initialized, it will be created automatically.
curl -X POST http://localhost:3000/api/knowledge/docs/add \
-H "Content-Type: application/json" \
-d '{
"documents": [
{
"content": "Your document content here",
"metadata": {
"source": "api",
"category": "example",
"timestamp": "2026-01-29"
}
}
]
}'
Response:
{
"success": true,
"added": 1
}
Use cases:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/functions |
List all functions |
| GET | /api/functions/:name |
Get function details and schema |
| POST | /api/functions/:name/call |
Call a function |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/skills |
List all skills (name, description) |
| GET | /api/skills/:name |
Get skill details (name, description, content) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/tasks |
List all tasks (filter by ?status=, ?kind=, ?target=) |
| GET | /api/tasks/:id |
Get task status and result |
| POST | /api/tasks/agent |
Submit an agent task |
| POST | /api/tasks/workflow |
Submit a workflow task |
| POST | /api/tasks/:id/cancel |
Cancel a running task |
| POST | /api/tasks/:id/respond |
Respond to an input-required task (ReAct human-in-the-loop) |
| GET | /api/tasks/:id/stream |
Stream task status updates (SSE) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/mcp |
List all MCP servers |
| GET | /api/mcp/:name |
Get MCP server config |
| GET | /api/mcp/:name/tools |
List tools from server |
| POST | /api/mcp/:name/call |
Call a tool on server |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/files/tree |
Get project directory tree |
| GET | /api/files/read?path=... |
Read file contents |
| PUT | /api/files/write |
Write file contents (hot-reload) |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/llm |
List all LLM configurations |
| GET | /api/llm/:name |
Get LLM config details |
| POST | /api/llm/:name/chat |
Chat with LLM |
| POST | /api/llm/:name/stream |
Stream chat with LLM (SSE) |
Real-world examples to help you get started with common use cases.
This example shows an agent using a knowledge store for org chart queries with PII protection.
name: corporate
description: Friendly corporate assistant — answers questions about the org, people, departments
version: "1.0.0"
llm:
name: default
temperature: 0.3
prompt:
system: |
You are Orcha, a friendly corporate assistant. You help employees find information
about teams, people, projects, and the organization.
inputVariables:
- query
tools:
- knowledge:org-chart
publish:
enabled: true
memory:
enabled: true
maxLines: 50
output:
format: text
Orchestrating multiple agents to complete a complex task.
name: example-workflow
description: A workflow demonstrating multi-agent orchestration
version: "1.0.0"
input:
schema:
topic:
type: string
required: true
description: The topic to process
steps:
- id: get-time
agent: time
input:
timezone: "America/New_York"
output:
key: current_time
- id: search-knowledge
agent: knowledge
input:
query: "{{input.topic}}"
output:
key: knowledge_result
- id: summarize
agent: example
input:
query: |
Summarize: {{steps.get-time.output}}
Topic: {{input.topic}}
Knowledge: {{steps.search-knowledge.output}}
output:
key: final_summary
config:
timeout: 300000
onError: stop
output:
timestamp: "{{steps.get-time.output}}"
knowledge: "{{steps.search-knowledge.output}}"
summary: "{{steps.summarize.output}}"
Setting up semantic search with a knowledge store. All data is persisted to SQLite automatically.
name: docs
description: Knowledge base for semantic search
source:
type: directory
path: knowledge/sample-data
pattern: "*.txt"
loader:
type: text
splitter:
type: character
chunkSize: 1000
chunkOverlap: 200
embedding: default
search:
defaultK: 4
scoreThreshold: 0.2
An autonomous workflow using the ReAct pattern with automatic tool and agent discovery.
name: react-research
description: Autonomous research using tool discovery
version: "1.0.0"
type: react
input:
schema:
topic:
type: string
required: true
prompt:
system: |
You are a research assistant with access to tools and agents.
Identify all tools you need, call them in parallel,
then synthesize results into a comprehensive report.
goal: "Research and analyze: {{input.topic}}"
graph:
model: default
executionMode: single-turn
tools:
mode: all
sources: [mcp, knowledge, function, builtin]
agents:
mode: all
maxIterations: 10
timeout: 300000
output:
analysis: "{{state.messages[-1].content}}"
Building a knowledge graph from a CSV file using direct mapping. Entities and relationships are extracted deterministically from columns.
name: org-chart
description: Company org chart with personnel directory and performance reviews
source:
type: file
path: knowledge/org-chart/personnel.csv
loader:
type: csv
splitter:
type: character
chunkSize: 800
chunkOverlap: 0
embedding: default
search:
defaultK: 5
graph:
directMapping:
entities:
- type: Department
idColumn: department_id
nameColumn: department_name
properties: []
- type: Employee
idColumn: employee_id
nameColumn: employee_name
properties:
- title
- location
- hire_date
- status
- rating
relationships:
- type: WORKS_IN
source: Employee
target: Department
sourceIdColumn: employee_id
targetIdColumn: department_id
- type: REPORTS_TO
source: Employee
target: Employee
sourceIdColumn: employee_id
targetIdColumn: manager_id
An agent that explores a music catalog via knowledge graph and handles email inquiries.
name: music-librarian
description: Music store expert — explore the catalog, discover artists, and analyze customer listening patterns
version: "1.0.0"
llm:
name: default
temperature: 0.6
prompt:
system: |
You are a Music Librarian for a digital music store. You have deep access to the store's
full catalog and purchase history through a knowledge graph.
Use semantic search, entity lookup, and graph traversal to answer questions.
inputVariables:
- query
tools:
- knowledge:music-store
integrations:
- type: email
imap:
host: mail.example.com
port: 993
secure: true
smtp:
host: mail.example.com
port: 587
secure: false
auth:
user: librarian
pass: secret
fromName: "Music Librarian"
fromAddress: librarian@example.com
pollInterval: 20
memory:
enabled: true
maxLines: 50
publish:
enabled: true