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

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:

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

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