Skip to main content
Agents are the conversational core of the platform. Each agent wraps an LLM with a system prompt, tools, knowledge bases, and optional MCP servers. Agents use a ReAct loop to reason about user messages, call tools as needed, and generate streaming responses. Sessions maintain conversation history across multiple turns.

Common Patterns

Create an agent, assign tools and knowledge bases, then use the streaming endpoint for conversations. Pass session_id to continue multi-turn conversations. For human-in-the-loop workflows, configure hooks and use the approve endpoint when approval_requested events are received.

Agent CRUD

GET /api/agents

List all agents.
response = requests.get(f"{BASE_URL}/api/agents", headers=headers)

POST /api/agents

Create a new agent.
{
  "name": "My Agent",
  "model": "gpt-4o",
  "system_prompt": "You are a helpful assistant.",
  "settings": { "temperature": 0.7 }
}
response = requests.post(f"{BASE_URL}/api/agents", headers=headers, json={"name": "My Agent", "model": "gpt-4o", "system_prompt": "You are helpful.", "settings": {"temperature": 0.7}})

GET /api/agents/

Get an agent by ID.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}", headers=headers)

PATCH /api/agents/

Update an agent’s name, model, system prompt, or settings.
id
string
required
Agent ID
response = requests.patch(f"{BASE_URL}/api/agents/{agent_id}", headers=headers, json={"settings": {"temperature": 0.5}})

DELETE /api/agents/

Delete an agent.
id
string
required
Agent ID
response = requests.delete(f"{BASE_URL}/api/agents/{agent_id}", headers=headers)

Tool Assignments

POST /api/agents//tools

Assign a tool to the agent.
id
string
required
Agent ID
{ "tool_name": "database_query" }
response = requests.post(f"{BASE_URL}/api/agents/{agent_id}/tools", headers=headers, json={"tool_name": "database_query"})

GET /api/agents//tools

List tool assignments for the agent.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}/tools", headers=headers)

PATCH /api/agents//tools/

Update a tool assignment’s config_override.
id
string
required
Agent ID
assignment_id
string
required
Assignment ID
config_override
object
required
The full config_override object. Tool-specific shape; for database tools include schemas: { <schema_name>: [<table_name>, ...] }.
The body MUST use the key config_override. Passing config (or any other top-level key) is a silent no-op — the route returns 200 with the unchanged assignment because the handler only inspects config_override.How config_override works per tool type:
  • Database tools (database_query, database_write)config_override.schemas: { <schema>: [<table>, ...] } controls which tables the agent can read/write. Schemas must be a dict, schema names must match ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$, system schemas (ai, auth, storage, pg_*, etc.) are rejected, and each value must be a list of valid table names.
  • All other builtin tools (web_search, web_scrape, http_request, code_execute, storage_read, storage_write) — any key in config_override whose name matches a parameter in the tool’s input_schema is force-injected into every call’s arguments. Keys not in the schema are silently dropped (no error, no warning). For example, setting config_override: { "max_results": 3, "include_domains": ["example.com"] } on a web_search assignment locks every search the agent does to 3 results from example.com. This is how you constrain a tool without changing what the agent is told about it — the LLM still sees the full schema, but its requested values for those keys are overridden.
  • Custom toolsconfig_override is not used. Custom-tool behavior is controlled via the Tool row’s own config (endpoint, method, headers) at create time.
  • MCP toolsconfig_override is not used. MCP-tool behavior is controlled via the AgentMcpServer row’s headers and enabled flag.
{
  "config_override": {
    "schemas": {
      "public": ["users", "orders"]
    }
  }
}
response = requests.patch(f"{BASE_URL}/api/agents/{agent_id}/tools/{assignment_id}", headers=headers, json={"config_override": {"schemas": {"public": ["users", "orders"]}}})

DELETE /api/agents//tools/

Remove a tool assignment from the agent.
id
string
required
Agent ID
assignment_id
string
required
Assignment ID
response = requests.delete(f"{BASE_URL}/api/agents/{agent_id}/tools/{assignment_id}", headers=headers)

Knowledge Base Assignments

POST /api/agents//knowledge-bases

Link a knowledge base to the agent. Creates a dynamic search tool.
id
string
required
Agent ID
{ "knowledge_base_id": "kb-uuid" }
response = requests.post(f"{BASE_URL}/api/agents/{agent_id}/knowledge-bases", headers=headers, json={"knowledge_base_id": kb_id})

GET /api/agents//knowledge-bases

List knowledge base assignments for the agent.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}/knowledge-bases", headers=headers)

DELETE /api/agents//knowledge-bases/

Remove a knowledge base from the agent.
id
string
required
Agent ID
assignment_id
string
required
Assignment ID
response = requests.delete(f"{BASE_URL}/api/agents/{agent_id}/knowledge-bases/{assignment_id}", headers=headers)

MCP Servers

POST /api/agents//mcp-servers

Add an MCP server to the agent.
id
string
required
Agent ID
{
  "url": "https://mcp.example.com",
  "transport": "http",
  "name": "My MCP"
}
response = requests.post(f"{BASE_URL}/api/agents/{agent_id}/mcp-servers", headers=headers, json={"url": "https://mcp.example.com", "transport": "http", "name": "My MCP"})

GET /api/agents//mcp-servers

List MCP servers for the agent.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}/mcp-servers", headers=headers)

PUT /api/agents//mcp-servers/

Update an MCP server configuration.
id
string
required
Agent ID
server_id
string
required
MCP Server ID
response = requests.put(f"{BASE_URL}/api/agents/{agent_id}/mcp-servers/{server_id}", headers=headers, json={"url": "https://new-url.com"})

DELETE /api/agents//mcp-servers/

Remove an MCP server from the agent.
id
string
required
Agent ID
server_id
string
required
MCP Server ID
response = requests.delete(f"{BASE_URL}/api/agents/{agent_id}/mcp-servers/{server_id}", headers=headers)

Hooks

POST /api/agents//hooks

Add a hook to the agent (pre/post execution events).
id
string
required
Agent ID
response = requests.post(f"{BASE_URL}/api/agents/{agent_id}/hooks", headers=headers, json={"event": "before_run", "type": "webhook", "config": {"url": "https://example.com/hook"}})

GET /api/agents//hooks

List hooks for the agent.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}/hooks", headers=headers)

DELETE /api/agents//hooks/

Remove a hook from the agent.
id
string
required
Agent ID
hook_id
string
required
Hook ID
response = requests.delete(f"{BASE_URL}/api/agents/{agent_id}/hooks/{hook_id}", headers=headers)

Execution

GET /api/agents//sessions

List chat sessions for the agent.
id
string
required
Agent ID
response = requests.get(f"{BASE_URL}/api/agents/{agent_id}/sessions", headers=headers)

POST /api/agents//run

Run agent synchronously, returning the full response as JSON. No streaming, no tools, no ReAct loop — a single LLM call against the conversation context. Useful for simple question-answering. If you need tool use, use /run/stream. The endpoint accepts the same context-selection surface as /run/stream: you can attach ad-hoc retrieval (knowledge_bases), a pre-built context (context_handler_id), a raw context string (context_override), or by-reference items (context_items). Only one context source may be provided per request — sending more than one returns 400.
id
string
required
Agent ID
{
  "message": "Hello",
  "session_id": "optional-session-uuid",
  "knowledge_bases": [{ "id": "kb-uuid", "top_k": 5 }],
  "max_context_tokens": 8000,
  "citations_enabled": false
}
Body fieldTypeNotes
messagestring (required)User input
session_idstringReuse a session for multi-turn. Omit to start a fresh session — session_id is returned.
knowledge_basesarrayAd-hoc retrieval: [{ id, top_k?, similarity_threshold?, filter_metadata?, retrieval_method? }]
context_handler_idstringUse a pre-built context handler (see Context Handlers)
context_overridestringRaw context string injected verbatim — skips retrieval
context_itemsarrayBy-reference items: [{ id, ... }] or by-value items with text
max_context_tokensintTruncate retrieved context to this token budget (default from agent settings or platform default)
citations_enabledboolAppend a citation instruction and parse [1]-style refs from the response (default false)
temperaturefloatPer-run override; falls back to agent setting
response_formatobjectLiteLLM-compatible JSON-schema for structured output
response = requests.post(f"{BASE_URL}/api/agents/{agent_id}/run", headers=headers, json={"message": "Hello"})

POST /api/agents//run/stream

Run agent with streaming SSE. Supports tools, ReAct loop, and multi-turn via session_id. SSE event types include start, chunk, tool_call, tool_result, reasoning, reasoning_summary, and complete; the reasoning events surface the model’s internal thought stream when reasoning is enabled. Set reasoning_requested=true to request reasoning output for this run. The streaming endpoint accepts the same context-selection surface as /run above (knowledge_bases / context_handler_id / context_override / context_items, mutually exclusive), plus all the same max_context_tokens, citations_enabled, temperature, and response_format fields. See Streaming for the full SSE event catalog.
id
string
required
Agent ID
{
  "message": "Hello",
  "session_id": "optional-session-uuid",
  "reasoning_requested": false,
  "knowledge_bases": [{ "id": "kb-uuid", "top_k": 5 }]
}
response = requests.post(
    f"{BASE_URL}/api/agents/{agent_id}/run/stream",
    headers=headers,
    json={"message": "Hello", "reasoning_requested": True},
    stream=True,
)
for line in response.iter_lines():
    if line and line.decode().startswith("data: "):
        event = json.loads(line.decode()[6:])
        print(event)

GET /api/agents/runs/

Fetch a single agent run by run_id, independent of session context. Returns: id, run_id, session_id, parent_orchestration_run_id, parent_workflow_execution_id, status, input_messages, output_messages, content, usage, retrieved_context, error, started_at, completed_at, steps, events, tool_calls, reasoning_steps, and created_at. The parent fields are null for top-level runs; session_id is null for delegated or workflow-block runs. For runs that belong to a session, ownership is enforced — the caller must own the session (or be using a service-role key). To avoid leaking existence, ownership failures are returned as 404, not 403.
run_id
string
required
Run ID
response = requests.get(f"{BASE_URL}/api/agents/runs/{run_id}", headers=headers)

POST /api/agents/runs//approve

Approve or deny a pending tool call (human-in-the-loop).
run_id
string
required
Run ID
{ "approved": true }
response = requests.post(f"{BASE_URL}/api/agents/runs/{run_id}/approve", headers=headers, json={"approved": True})

Error Responses

Agent routes return {"error": "<message>"}.
StatusDescription
400Missing or invalid required field (e.g. name, tool_type/tool_name, knowledge_base_id, MCP server name/url, hook event/type/config)
400Invalid config_override.schemas on tool PATCH (e.g. not a dict, contains a system schema, or has invalid table names)
404No agent, tool/KB/MCP/hook assignment, or run exists with the given ID
404/run or /run/stream: the supplied session_id is not owned by the caller (returned as 404 to avoid leaking existence)
409Knowledge base already assigned to this agent
409An MCP server with this name already exists for this agent