API Reference¶
Complete endpoint documentation for the FastAPI router created by
create_agent_router(). All endpoints are prefixed with the prefix
argument (default /api/v1).
Authentication¶
All endpoints require the X-Tenant-ID header for multi-tenant isolation.
| Header | Required | Description |
|---|---|---|
X-Tenant-ID |
Yes | Unique tenant identifier |
X-Sub-Tenant-ID |
No | Sub-tenant for hierarchical tenancy |
X-Namespace |
No | Memory namespace override |
Missing X-Tenant-ID returns 401 Unauthorized.
Chat Endpoints¶
POST /api/v1/chat¶
Execute the agent synchronously and return the full response.
Request Body (AgentRequest):
{
"query": "What do you know about our brand?",
"tenant_id": "acme-corp",
"sub_tenant_id": null,
"namespace": null,
"session_id": null,
"metadata": null
}
| Field | Type | Required | Description |
|---|---|---|---|
query |
string |
Yes | The user message (min 1 char) |
tenant_id |
string |
Yes | Tenant identifier (min 1 char) |
sub_tenant_id |
string |
No | Sub-tenant identifier |
namespace |
string |
No | Memory namespace |
session_id |
string |
No | Session ID for conversation continuity |
metadata |
object |
No | Arbitrary key-value metadata |
Response (AgentResponse):
{
"final_response": "Your brand voice is friendly and expert...",
"messages": [],
"memory_entries_used": 3,
"system_prompt_tokens": 1250,
"run_id": "a1b2c3d4-...",
"duration_ms": 1423.5,
"extracted_ops": [
{"type": "upsert_node", "label": "SOUL", "properties": {"brand_voice": "friendly"}}
],
"graph_edges": [
{"source": "SOUL", "target": "BRAND", "relationship": "DEFINES"}
],
"activation_log": {
"activated_nodes": [{"name": "SOUL", "score": 0.92}],
"inference_paths": [["SOUL", "BRAND"]],
"pending_connections": []
}
}
| Field | Type | Description |
|---|---|---|
final_response |
string |
The agent's text response |
messages |
list |
Raw LangChain message history |
memory_entries_used |
int |
Number of memory entries injected |
system_prompt_tokens |
int |
Token count of the system prompt |
run_id |
string |
Unique execution identifier |
duration_ms |
float |
Total execution time in milliseconds |
extracted_ops |
list |
Memory operations extracted from the response |
graph_edges |
list |
Knowledge graph edges created or referenced |
activation_log |
object |
Spreading activation metadata |
Error Codes: 400 (invalid scope), 403 (security scope error), 500 (agent failure)
Example:
curl -X POST http://localhost:8000/api/v1/chat \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"query": "What is our brand voice?", "tenant_id": "acme-corp"}'
POST /api/v1/stream¶
Stream agent execution as Server-Sent Events (SSE). Same request body as
/chat, but the response is an SSE event stream.
As of v5.3.0, text chunks are filtered through ExtractionFilter --
<MEMORY_EXTRACT> and <GRAPH_OPERATIONS> blocks are never sent to the
client. The done event includes the stripped final_response.
Event Types:
| Event | Description |
|---|---|
thinking |
Agent is processing (extended thinking content) |
acting |
Agent is executing a tool |
text_delta |
Incremental text chunk from the LLM (extraction tags stripped) |
tool_call |
Tool invocation details |
tool_result |
Tool execution result |
consolidating |
Memory consolidation in progress |
spreading_activation |
Activation expansion metadata |
memory_extracted |
Extraction blocks parsed from the response (v5.3.0) |
consolidation_done |
Background memory write complete (v5.3.0) |
response_complete |
All processing finished (v5.3.0) |
done |
Stream complete, final metadata |
error |
Error occurred during streaming |
StreamChunk Schema (each SSE data field):
{
"event_type": "text_delta",
"data": "partial text...",
"timestamp": "2026-03-30T12:00:00Z",
"run_id": "a1b2c3d4-..."
}
Example:
curl -N -X POST http://localhost:8000/api/v1/stream \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"query": "Analyze our sales data", "tenant_id": "acme-corp"}'
JavaScript Client:
const eventSource = new EventSource('/api/v1/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Tenant-ID': 'acme-corp',
},
body: JSON.stringify({ query: 'Hello!', tenant_id: 'acme-corp' }),
});
eventSource.addEventListener('text_delta', (e) => {
const chunk = JSON.parse(e.data);
document.getElementById('output').textContent += chunk.data;
});
eventSource.addEventListener('done', () => eventSource.close());
eventSource.addEventListener('error', (e) => console.error(e));
Session Endpoints¶
GET /api/v1/chats¶
List active chat sessions for the current tenant.
Response: Array of session objects.
[
{
"session_id": "sess-abc123",
"tenant_id": "acme-corp",
"created_at": "2026-03-30T10:00:00Z",
"message_count": 5
}
]
Example:
Memory Endpoints¶
GET /api/v1/memories/status¶
Return the status of all known memory blocks for the current tenant, grouped by label. Useful for dashboards showing memory health.
Response (MemoryBlockStatus[]):
[
{
"label": "SOUL",
"exists": true,
"entry_count": 6,
"last_updated": "2026-03-30T12:00:00Z",
"importance": 8.5,
"layer": "semantic",
"enabled": true,
"is_required": true
}
]
| Field | Type | Description |
|---|---|---|
label |
string |
Block label (e.g. SOUL, PRODUCT) |
exists |
bool |
Whether any entries exist for this label |
entry_count |
int |
Number of entries in the block |
last_updated |
datetime |
Most recent entry timestamp |
importance |
float |
Average importance score (0.0-10.0) |
layer |
string |
Memory layer name |
enabled |
bool |
Whether this layer is enabled in config |
is_required |
bool |
Whether the domain template requires this block |
Example:
GET /api/v1/memories¶
List all semantic memory nodes for the tenant, grouped by label prefix.
Response: Array of node objects.
[
{
"id": "node-uuid-...",
"label": "SOUL: brand_voice",
"group": "SOUL",
"properties": {"brand_voice": "friendly and expert"},
"importance": 9.0,
"created_at": "2026-03-30T10:00:00Z",
"updated_at": "2026-03-30T12:00:00Z"
}
]
Example:
PATCH /api/v1/memories/{node_id}¶
Update a memory node's content, properties, or importance. Adds an
audit trail (_last_edited_by: user_manual_edit).
Request Body (all fields optional):
{
"content": "Updated brand voice description",
"properties": {"brand_voice": "professional yet approachable"},
"importance": 9.5
}
Response:
{
"id": "node-uuid-...",
"label": "Updated brand voice description",
"properties": {"brand_voice": "professional yet approachable", "_last_edited_by": "user_manual_edit"}
}
Example:
curl -X PATCH http://localhost:8000/api/v1/memories/node-uuid-123 \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"importance": 9.5}'
DELETE /api/v1/memories/{node_id}¶
Delete a memory node and cascade-delete its edges.
Response:
Error: 404 if the node does not exist.
Example:
Procedural Endpoints (Workflows)¶
GET /api/v1/procedures¶
List all procedural nodes (learned workflows and skills).
Response:
[
{
"id": "proc-uuid-...",
"label": "Monthly Inventory Check",
"content": "Monthly Inventory Check",
"steps": ["Pull inventory levels", "Compare to reorder points", "Generate PO"],
"properties": {"active": true},
"active": true,
"importance": 7.0,
"created_at": "2026-03-29T08:00:00Z",
"updated_at": "2026-03-30T12:00:00Z"
}
]
Example:
PATCH /api/v1/procedures/{node_id}¶
Update a procedure's steps, properties, or active flag.
Request Body (all fields optional):
{
"steps": ["Step 1", "Step 2", "Step 3"],
"active": true,
"properties": {"category": "inventory"}
}
Response: Updated procedure object (same shape as list item).
Example:
curl -X PATCH http://localhost:8000/api/v1/procedures/proc-uuid-123 \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"steps": ["Check levels", "Generate PO", "Notify supplier"]}'
POST /api/v1/procedures/deduplicate¶
Merge semantically similar procedural nodes. Uses SemanticMerge with
a similarity threshold of 0.75. Absorbed duplicates are deleted;
canonical nodes are updated in-place.
Response:
{
"merged": 2,
"remaining": [
{
"id": "proc-uuid-...",
"label": "Monthly Inventory Check",
"content": "Monthly Inventory Check",
"steps": ["..."],
"active": true,
"importance": 7.0
}
]
}
Example:
POST /api/v1/procedures/{node_id}/toggle¶
Toggle a procedure's active flag (true becomes false and vice versa).
Response:
{
"id": "proc-uuid-123",
"label": "Monthly Inventory Check",
"active": false,
"properties": {"active": false, "_last_edited_by": "user_toggle"}
}
Example:
curl -X POST http://localhost:8000/api/v1/procedures/proc-uuid-123/toggle \
-H "X-Tenant-ID: acme-corp"
Consolidation Endpoints¶
POST /api/v1/memory/consolidate¶
Trigger manual Deep Sleep consolidation for the tenant. Runs a 7-phase process: replay traces, strengthen recurring nodes, prune orphans, generate meta-nodes from clusters, update SOUL schema from user corrections, and write pending inferred edges.
Request Body (optional):
The pending_connections array is typically obtained from the
activation_log.pending_connections field returned by /chat.
Response (ConsolidationReport):
{
"strengthened_count": 4,
"pruned_count": 1,
"meta_nodes_created": 2,
"edges_written": 3,
"soul_updates": 1,
"duration_ms": 245.3
}
Example:
curl -X POST http://localhost:8000/api/v1/memory/consolidate \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{}'
Graph Service Endpoints¶
Added in v5.4.0. These endpoints are mounted by create_graph_router() and
provide graph traversal, pathfinding, edge CRUD, and cluster projections.
All endpoints require the X-Tenant-ID header.
GET /api/v1/graph/nodes/{node_id}/neighborhood¶
Return a node's neighborhood up to depth hops via BFS traversal.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
depth |
int |
2 |
Maximum traversal depth |
min_weight |
float |
0 |
Minimum edge weight to include |
Response (NeighborhoodResponse):
{
"center": {"id": "node-1", "label": "SOUL: brand_voice", "layer": "semantic", "importance": 9.0},
"neighbors": [
{"id": "node-2", "label": "BRAND: voice", "layer": "semantic", "importance": 7.5, "depth": 1}
],
"edges": [
{"id": "edge-1", "source": "node-1", "target": "node-2", "relationship": "DEFINES", "weight": 1.0}
]
}
| Field | Type | Description |
|---|---|---|
center |
object |
The queried node (id, label, layer, importance) |
neighbors |
NeighborNode[] |
Discovered neighbor nodes with BFS depth |
edges |
EdgeData[] |
Edges connecting the traversed subgraph |
Error Codes: 400 (invalid scope), 403 (access denied), 404 (node not found or semantic layer unavailable), 500 (internal error)
Example:
curl http://localhost:8000/api/v1/graph/nodes/node-uuid-123/neighborhood?depth=2\&min_weight=0.5 \
-H "X-Tenant-ID: acme-corp"
GET /api/v1/graph/paths¶
Find the shortest path between two nodes in the knowledge graph.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
start |
string |
Yes | Source node ID |
end |
string |
Yes | Target node ID |
Response (PathResponse):
{
"path": [
{"id": "node-1", "label": "SOUL", "layer": "semantic"},
{"id": "node-2", "label": "BRAND", "layer": "semantic"}
],
"edges": [
{"id": "edge-1", "source": "node-1", "target": "node-2", "relationship": "DEFINES", "weight": 1.0}
],
"hops": 1
}
| Field | Type | Description |
|---|---|---|
path |
PathNode[] |
Ordered list of nodes from start to end |
edges |
EdgeData[] |
Connecting edges between consecutive path nodes |
hops |
int |
Number of edges in the path (0 if no path found) |
Returns an empty path (hops: 0) when no path exists.
Error Codes: 400 (invalid scope), 403 (access denied), 404 (start or end node not found), 500 (internal error)
Example:
curl "http://localhost:8000/api/v1/graph/paths?start=node-1&end=node-2" \
-H "X-Tenant-ID: acme-corp"
GET /api/v1/edges¶
List edges for the current tenant with optional pagination and filtering.
Query Parameters (added in v5.5.0):
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
int |
50 |
Maximum number of edges to return |
offset |
int |
0 |
Number of edges to skip (for pagination) |
relationship |
string |
null |
Filter by relationship type (e.g. CO_OCCURRED) |
Response: Array of EdgeData objects.
[
{"id": "edge-1", "source": "node-1", "target": "node-2", "relationship": "DEFINES", "weight": 1.0}
]
Example:
curl http://localhost:8000/api/v1/edges \
-H "X-Tenant-ID: acme-corp"
# With pagination and filtering
curl "http://localhost:8000/api/v1/edges?limit=20&offset=0&relationship=CO_OCCURRED" \
-H "X-Tenant-ID: acme-corp"
POST /api/v1/edges¶
Create a new edge between two existing nodes. If an edge with the same
source, target, and relationship already exists, its weight is incremented
by 1 (upsert semantics, added in v5.5.0). Postgres uses
ON CONFLICT DO UPDATE SET weight = weight + 1. Weight is capped at
MAX_CO_OCCUR_DEGREE = 10 to prevent unbounded growth.
Request Body (CreateEdgeRequest):
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
source |
string |
Yes | -- | Source node ID |
target |
string |
Yes | -- | Target node ID |
relationship |
string |
Yes | -- | Edge relationship type (e.g. DEFINES, CO_OCCURRED) |
weight |
float |
No | 1.0 |
Edge weight (must be > 0) |
Response: The created EdgeData object with server-assigned id.
Example:
curl -X POST http://localhost:8000/api/v1/edges \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"source": "node-1", "target": "node-2", "relationship": "DEFINES", "weight": 1.0}'
DELETE /api/v1/edges/{edge_id}¶
Delete an edge by ID.
Response:
Example:
GET /api/v1/graph/clusters¶
Project all semantic nodes into clusters grouped by label prefix. Each cluster includes a representative (highest-importance member).
Response: Array of ClusterProjection objects.
[
{
"prefix": "SOUL",
"count": 3,
"members": [
{"id": "node-1", "label": "SOUL: brand_voice"},
{"id": "node-2", "label": "SOUL: name"},
{"id": "node-3", "label": "SOUL: tone"}
],
"representative": {"id": "node-1", "label": "SOUL: brand_voice"}
}
]
| Field | Type | Description |
|---|---|---|
prefix |
string |
The label prefix used for grouping |
count |
int |
Number of nodes in the cluster |
members |
ClusterMember[] |
All nodes in the cluster |
representative |
ClusterMember |
Highest-importance member (or null) |
Example:
Bulk Operations¶
Added in v5.5.0. Batch operations for edge and memory cleanup.
POST /api/v1/edges/bulk-delete¶
Delete multiple edges in a single request.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
ids |
string[] |
Yes | List of edge IDs to delete |
Response:
Example:
curl -X POST http://localhost:8000/api/v1/edges/bulk-delete \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"ids": ["edge-1", "edge-2", "edge-3"]}'
POST /api/v1/memories/bulk-delete¶
Delete multiple memory nodes in a single request. Edges connected to deleted nodes are cascade-deleted.
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
ids |
string[] |
Yes | List of memory node IDs to delete |
Response:
Example:
curl -X POST http://localhost:8000/api/v1/memories/bulk-delete \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"ids": ["node-1", "node-2"]}'
Graph Maintenance Endpoints¶
Added in v5.5.0. Audit and prune operations for knowledge graph health.
GET /api/v1/memory/audit¶
Analyze graph health: detect orphan nodes, weak edges, and calculate graph density metrics.
Response (AuditReport):
{
"total_nodes": 42,
"total_edges": 87,
"orphan_nodes": [
{"id": "node-15", "label": "PRODUCT: legacy_sku"}
],
"weak_edges": [
{"id": "edge-33", "source": "node-5", "target": "node-15", "weight": 0.1}
],
"density": 0.10,
"avg_edge_weight": 2.3
}
| Field | Type | Description |
|---|---|---|
total_nodes |
int |
Total node count for the tenant |
total_edges |
int |
Total edge count for the tenant |
orphan_nodes |
NodeSummary[] |
Nodes with zero edges |
weak_edges |
EdgeData[] |
Edges below the default weight threshold |
density |
float |
Graph density (edges / possible edges) |
avg_edge_weight |
float |
Mean edge weight across all edges |
Example:
POST /api/v1/memory/prune¶
Remove orphan nodes and weak edges from the knowledge graph. Supports dry-run mode to preview changes without applying them.
Request Body (all fields optional):
| Field | Type | Default | Description |
|---|---|---|---|
prune_orphans |
bool |
true |
Remove nodes with zero edges |
prune_weak_edges |
bool |
true |
Remove edges below min_edge_weight |
min_edge_weight |
float |
1.0 |
Minimum weight threshold for edge retention |
dry_run |
bool |
false |
If true, return what would be pruned without applying changes |
Response (PruneReport):
| Field | Type | Description |
|---|---|---|
orphans_removed |
int |
Number of orphan nodes removed (or that would be) |
edges_removed |
int |
Number of weak edges removed (or that would be) |
dry_run |
bool |
Whether this was a dry run |
Example:
# Preview what would be pruned
curl -X POST http://localhost:8000/api/v1/memory/prune \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"dry_run": true}'
# Actually prune weak edges with custom threshold
curl -X POST http://localhost:8000/api/v1/memory/prune \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: acme-corp" \
-d '{"prune_orphans": false, "prune_weak_edges": true, "min_edge_weight": 2.0}'
Demo-Only Endpoints¶
The following endpoints are provided by the full demo application
(examples/full_demo/server.py) and are not part of the core
create_agent_router(). They serve as reference implementations.
GET /api/v1/memory/graph¶
Returns knowledge graph data formatted for vis.js visualization. Nodes are colored by layer and sized by importance.
Response:
{
"nodes": [
{"id": "node-1", "label": "SOUL: brand_voice", "group": "semantic", "value": 9.0}
],
"edges": [
{"from": "node-1", "to": "node-2", "label": "DEFINES"}
]
}
Example:
GET /api/v1/memory/metrics¶
Returns memory layer metrics: entry counts, average importance, and last-updated timestamps per layer.
Response:
{
"semantic": {"count": 12, "avg_importance": 7.3},
"episodic": {"count": 45, "avg_importance": 5.1},
"working": {"count": 3, "avg_importance": 6.0},
"procedural": {"count": 8, "avg_importance": 7.0},
"prospective": {"count": 2, "avg_importance": 6.5}
}
Example:
Error Responses¶
All endpoints return standard HTTP error codes with a JSON body:
| Code | Meaning |
|---|---|
| 400 | Invalid scope or request validation failure |
| 401 | Missing X-Tenant-ID header |
| 403 | Security scope error (invalid tenant access) |
| 404 | Resource not found (node, layer) |
| 500 | Internal server error or agent execution failure |