Skip to content

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:

curl http://localhost:8000/api/v1/chats \
  -H "X-Tenant-ID: acme-corp"

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:

curl http://localhost:8000/api/v1/memories/status \
  -H "X-Tenant-ID: acme-corp"

GET /api/v1/memories

The kernel router lists committed memories visible to the authenticated scope. Use layer to narrow the layer, limit (1–500; default 200) to bound a page, and cursor to continue. Records are ordered by record ID, not recall score. Pending and retracted graph records are excluded before pagination.

The response remains an array. X-Memory-Has-More: true means another page is available; pass X-Memory-Next-Cursor verbatim as the next request's cursor. Keep the same scope and layer filter. No cursor header means the traversal has finished. Invalid cursors or limits return 400 (malformed integer input returns 422); a custom store without inventory support returns 501 explicitly.

Pagination is a keyset traversal of a stable set, not a snapshot. Concurrent inserts behind the cursor require restarting the listing. Deleting an earlier record does not shift later pages. Listing pages do not constrain recall or spreading activation; those retain their independent scopes and budgets.

Response: Array of node objects.

[
  {
    "id": "node-uuid-...",
    "layer": "semantic",
    "content": "Brand voice is friendly and expert",
    "scope_path": "acme-corp",
    "salience": 0.9,
    "origin": "extraction",
    "revision": "example-revision",
    "metadata": {}
  }
]

Example:

curl http://localhost:8000/api/v1/memories \
  -H "X-Tenant-ID: acme-corp"

To inspect continuation headers, use curl -i; then request the next page with curl --get --data-urlencode 'cursor=TOKEN' --data 'limit=200' and the same authentication headers. The generated Brain graph uses the separate /graph/nodes and /edges APIs.

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:

{"deleted": "node-uuid-123"}

Error: 404 if the node does not exist.

Example:

curl -X DELETE http://localhost:8000/api/v1/memories/node-uuid-123 \
  -H "X-Tenant-ID: acme-corp"

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:

curl http://localhost:8000/api/v1/procedures \
  -H "X-Tenant-ID: acme-corp"

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:

curl -X POST http://localhost:8000/api/v1/procedures/deduplicate \
  -H "X-Tenant-ID: acme-corp"

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):

{
  "pending_connections": [
    {"source": "SOUL", "target": "BRAND", "relationship": "ASSOCIATED"}
  ]
}

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):

{
  "source": "node-1",
  "target": "node-2",
  "relationship": "DEFINES",
  "weight": 1.0
}
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:

{"deleted": "edge-1"}

Example:

curl -X DELETE http://localhost:8000/api/v1/edges/edge-1 \
  -H "X-Tenant-ID: acme-corp"

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:

curl http://localhost:8000/api/v1/graph/clusters \
  -H "X-Tenant-ID: acme-corp"

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:

{
  "ids": ["edge-1", "edge-2", "edge-3"]
}
Field Type Required Description
ids string[] Yes List of edge IDs to delete

Response:

{
  "deleted": 3
}

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:

{
  "ids": ["node-1", "node-2"]
}
Field Type Required Description
ids string[] Yes List of memory node IDs to delete

Response:

{
  "deleted": 2
}

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:

curl http://localhost:8000/api/v1/memory/audit \
  -H "X-Tenant-ID: acme-corp"

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):

{
  "prune_orphans": true,
  "prune_weak_edges": true,
  "min_edge_weight": 1.0,
  "dry_run": false
}
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):

{
  "orphans_removed": 3,
  "edges_removed": 7,
  "dry_run": false
}
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:

curl http://localhost:8000/api/v1/memory/graph \
  -H "X-Tenant-ID: acme-corp"

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:

curl http://localhost:8000/api/v1/memory/metrics \
  -H "X-Tenant-ID: acme-corp"

Error Responses

All endpoints return standard HTTP error codes with a JSON body:

{"detail": "Error description"}
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