API & Extension Points
The HTTP/SSE surface of the Kazma Web UI, the SSE event contract, and the concrete places to extend the framework (tools, providers, adapters, skills, MCP).
1. HTTP API surface
Section titled “1. HTTP API surface”All endpoints are mounted by KazmaAppBuilder in kazma-ui/kazma_ui/app.py:615-709. Routers:
| Router | Prefix/area | Source |
|---|---|---|
health_router | /health/* | health.py |
chat_router | page routes (/chat, …) | chat.py |
settings_router | /settings | settings.py |
skills_router | skills | skills routes |
mcp_router | MCP | mcp routes |
agents_router | agents | agents routes |
providers_router | /api/providers | providers routes |
sse_router | /api/chat/* | sse_chat.py |
telemetry_router | telemetry | telemetry routes |
dashboard_router | /api/dashboard/* | dashboard.py |
models_router | models | models routes |
workspace_router | workspace | workspace routes |
swarm_router | /api/swarm/* | swarm_panel/ |
monitor_router | monitor | monitor routes |
metrics_router | metrics | metrics routes |
Plus direct routes in routes_direct.py and a conditional Telegram webhook at /api/webhooks/telegram (app.py:365).
2. Key endpoints (verified)
Section titled “2. Key endpoints (verified)”2.1 Chat (SSE)
Section titled “2.1 Chat (SSE)”| Method | Path | Purpose |
|---|---|---|
POST | /api/chat/stream | Primary chat transport. Body \{message, session_id, model\}. Returns text/event-stream. (sse_chat.py:353) |
GET | /api/chat/sessions | List sessions. (line 547) |
DELETE | /api/chat/sessions/\{session_id\} | Delete session. (line 555) |
GET | /api/chat/sessions/\{session_id\}/messages | Session history. (line 561) |
Legacy:
GET /ws/chatreturns 410 Gone (chat.py:4). Do not use.
2.2 Providers
Section titled “2.2 Providers”| Method | Path | Purpose |
|---|---|---|
GET | /api/provider/active | Active provider/model. (line 583) |
GET | /api/providers | List providers. (line 601) |
POST | /api/provider/switch | Switch active provider/model. (line 607) |
2.3 HITL approval
Section titled “2.3 HITL approval”| Method | Path | Purpose |
|---|---|---|
GET | /api/pending-approvals | Pending HITL approvals. (hitl_approval.py:146) |
POST | /api/approve/\{thread_id\} | Approve/deny a paused tool. Body `{action: “approve" |
2.4 Dashboard
Section titled “2.4 Dashboard”| Method | Path | Purpose |
|---|---|---|
GET | /api/dashboard/status | Dashboard overview. (dashboard.py:177) |
GET | /api/sessions | Sessions list. (line 221) |
POST | /api/sessions/clear-all | Clear sessions. (line 330) |
2.5 Swarm
Section titled “2.5 Swarm”| Method | Path | Purpose |
|---|---|---|
GET | /api/swarm/status | Swarm status. |
GET/POST/DELETE | /api/swarm/workers[/\{name\}] | Worker CRUD. |
POST | /api/swarm/dispatch | Dispatch a task (all patterns via type). |
GET | /api/swarm/tasks[/\{id\}] | Task list / detail. |
POST | /api/swarm/tasks/\{id\}/approve | Approve pipeline checkpoint. (routes_tasks.py:612) |
POST | /api/swarm/tasks/\{id\}/reject | Reject pipeline checkpoint. (line 657) |
GET | /api/swarm/workers/\{name\}/metrics | Worker metrics. |
GET | /api/swarm/circuit-breakers | Breaker states. |
2.6 Memory (V2 cognitive engine)
Section titled “2.6 Memory (V2 cognitive engine)”V2 is the only memory stack after the V1→V2 cutover (memory.v2.use_new_stack: true). The V2 routes below return shaped JSON on error (never a bare 500); non-numeric params yield a FastAPI 422. /api/system/status returns a top-level memory_stack field ("v2") plus a v2 KPI block so the dashboard surfaces V2 counts. See Memory & RAG for the stack model.
Core routes (routes_direct.py):
| Method | Path | Purpose |
|---|---|---|
GET | /api/memory/v2/health | V2 health snapshot — active/superseded/archived belief counts, episode/entity/procedural stats, queue depth. Drives the dashboard KPI grid (pollV2Health, 5s cadence). |
GET | /api/memory/v2/beliefs | Active beliefs list. ?q= FTS filter, ?limit= (default 50, clamped 1–200). |
POST | /api/memory/v2/beliefs/{id}/invalidate | Soft-invalidate one belief (+ best-effort Neo4j edge delete). |
POST | /api/memory/v2/beliefs/invalidate-batch | Soft-invalidate many ({ "ids": [...] }). |
PATCH | /api/memory/v2/beliefs/{id} | Operator edit of active triple: optional subject, predicate, object, predicate_type. Sets extraction_method=user_explicit; clears embedding if object changes. |
GET | /api/memory/v2/graph | Belief graph \{nodes, links, stats, groups\} for the canvas. Bi-temporal + filter params: ?at=<unix_ts> (point-in-time scrub; superseded beliefs marked superseded=true), ?type= (functional/set/state predicate_type), ?entity_type= (person/tool/concept/…), ?limit= (default 200), ?source=neo4j (optional probe). stats.total_links vs stats.links is the slicing delta shown on the truncation banner. Invariants: unique node ids; no virtual fact node when object text equals an entity id; no dangling links; hub node id=user with display name from entities.user (self person shells collapsed onto hub); payload-object subjects carry a hub related_to anchor. |
GET/POST/DELETE | /api/memory/v2/graph/groups* | View-only groupings (list/create/delete/move/tier). Never mutates beliefs. Canvas poll uses groups on GET /graph; Ungroup is DELETE …/groups/{id}. |
GET | /api/memory/v2/graph/export | On-demand JSON or GraphML (?format=json|graphml). |
GET | /api/memory/v2/entities | Entity list for /memory ops. Flags: empty, isolated, protected, is_self, graph_id (self shells → "user"). Query: ?q=, ?empty_only=, ?isolated_only=, ?limit=. |
POST | /api/memory/v2/entities/{id}/rename | Display rename only ({ "name": "…" }). Id stable; aliases preserved. Self/person User shells also upsert hub entities.user. Returns hub_synced, graph_id. |
POST | /api/memory/v2/entities/merge | Merge source into target (beliefs rewired, aliases union). |
POST | /api/memory/v2/entities/link | Create belief edge (subject, predicate, object). |
DELETE | /api/memory/v2/entities/{id} | Delete entity shell (blocked for protected ids: user, assistant, …). Copies matching entity_merges rows to entity_merges_archive before dropping live ledger rows (FK). |
GET | /api/memory/v2/admin/summary | Counts for ops chips (live/invalidated beliefs, empty/isolated entities). |
GET/POST | /api/memory/v2/hygiene/* | Preview + run empty purge / near-dup invalidate / archive. |
GET/POST | /api/memory/v2/entity-merges* | Quarantine merge list + approve/reject. |
POST | /api/memory/v2/probe | Recall dry-run (explain chips). |
POST | /api/memory/v2/federated-search | Memory + KB labeled search. |
POST | /api/memory/v2/eval/golden | Golden recall suite. |
Legacy graph family (/api/memory/graph*) — the L2-style property-graph endpoints, now V2-backed and tenant-scoped:
| Method | Path | Purpose |
|---|---|---|
GET | /api/memory/graph | Property graph JSON (nodes/edges); optional ?q= filter. |
GET | /api/memory/graph/stats | Node/edge counts + backend path. |
GET | /api/memory/graph/search | FTS search over graph nodes (?q=&limit=). |
POST | /api/memory/graph/clear | Bi-temporal invalidate of active V2 beliefs for one tenant (?tenant= defaults to default; no all-tenants mode). Tombstones the PG mirror, deletes Neo4j edges, writes a graph_clear audit row. UI confirms. |
2.7 Health
Section titled “2.7 Health”| Method | Path | Purpose |
|---|---|---|
GET | /health/live | Liveness. (health.py:94) |
GET | /health/ready | Readiness. (line 104) |
GET | /health/details | Detailed health. (line 148) |
GET | /api/gateway/status | Gateway/adapter status. |
2.8 X publisher
Section titled “2.8 X publisher”| Method | Path | Purpose |
|---|---|---|
GET | /x | X Studio page (composer + X-only planner). |
GET | /api/x/status | Configured?, handle, caps. Never returns secrets. |
POST | /api/x/preview | Dry-run ToU policy. No network, no ledger. |
GET | /api/x/drafts | Flattened save_proposal items. |
GET | /api/x/audit | Recent x_audit.db rows. |
POST | /api/x/post | Immediate post. Operator click is the approval. |
POST | /api/x/delete | Delete a live tweet. Operator click is the approval. |
POST | /api/x/credentials | Save four OAuth 1.0a keys (vaulted) + handle + caps. |
POST | /api/x/test | GET /2/users/me with stored keys. |
POST | /api/x/disconnect | Delete keys, disable posting. |
POST / PUT / DELETE | /api/scheduled/x | Book / reschedule / cancel. All clocks is /scheduled. |
Chat tweets still go through x_post (always HITL + proposal_id). Guide: X Publisher.
3. SSE event contract {#sse-event-contract}
Section titled “3. SSE event contract {#sse-event-contract}”POST /api/chat/stream returns a stream of Server-Sent Events. Each event has a typed event: line and a JSON data: payload (sse_chat.py:8-13).
event: | Meaning | Key payload fields |
|---|---|---|
token | An LLM streaming chunk. | content |
tool_call | A tool is starting. | tool, args |
tool_result | A tool finished. | tool, result, is_error |
approval_required | A HITL pause surfaced — frontend should call POST /api/approve/\{thread_id\}. (line 199-207) | thread_id, tool, args |
done | Turn complete. | tokens, cost_usd, duration_ms |
error | Fatal error. | message |
HITL approval expiry: if the user clicks Approve/Deny on a card that
has already timed out or been resumed, POST /api/approve/{thread_id}
returns HTTP 409 with {"status": "expired", "error": "No pending approval for this thread (already resumed or expired)."}. The frontend
(hitl_approval.js) detects this and transitions the card to
“Expired or already resumed” then removes it.
3.1 Client-side example (JavaScript)
Section titled “3.1 Client-side example (JavaScript)”// chat.js uses KS.sse('/api/chat/stream', {...}); the raw shape is:const resp = await fetch('/api/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'Hello', session_id: sess, model: 'gpt-4o-mini' }),});
const reader = resp.body.getReader();const decoder = new TextDecoder();let buffer = '';
while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true });
// SSE events are separated by blank lines let idx; while ((idx = buffer.indexOf('\n\n')) !== -1) { const block = buffer.slice(0, idx); buffer = buffer.slice(idx + 2); const eventType = (block.match(/^event: (.+)$/m) || [])[1]; const data = JSON.parse(((block.match(/^data: (.+)$/m) || [])[1]) || '{}'); handleEvent(eventType, data); }}
function handleEvent(type, data) { switch (type) { case 'token': appendToken(data.content); break; case 'tool_call': showToolCall(data.tool, data.args); break; case 'tool_result': showToolResult(data.tool, data.result); break; case 'approval_required': promptApproval(data.thread_id, data.tool); break; case 'done': finishTurn(data.tokens, data.cost_usd); break; case 'error': showError(data.message); break; }}3.2 Approving via the API (Python)
Section titled “3.2 Approving via the API (Python)”import httpx
resp = httpx.post( "http://127.0.0.1:8000/api/approve/<thread_id>", headers={"X-Kazma-Secret": KAZMA_SECRET}, # required if KAZMA_SECRET is set json={"action": "approve", "reason": "looks safe"},)print(resp.status_code, resp.json())4. Extension points
Section titled “4. Extension points”4.1 Add a tool
Section titled “4.1 Add a tool”Register a function with the ToolRegistry:
from kazma_core.agent.tool_registry import register_tool
@register_tool( name="weather_lookup", description="Look up current weather for a city.", danger=False, # True → triggers HITL)async def weather_lookup(city: str) -> str: ... return f"Weather in {city}: sunny, 25C"Register during startup (or via a skill entry point). The supervisor exposes it to the LLM automatically.
4.2 Add a provider
Section titled “4.2 Add a provider”Providers are ConfigStore entries under providers.list. The 10 built-in presets are in kazma_core/providers.py:13-84. To add a custom OpenAI-compatible endpoint:
from kazma_core.config_store import get_config_storefrom kazma_core.model_registry import get_model_registry
store = get_config_store()reg = get_model_registry()
# Option A: use the 'custom' preset shapereg.upsert_provider( name="my-endpoint", display_name="My Inference Server", base_url="https://infer.example.com/v1", api_key="sk-...", enabled=True,)
# Option B: switch active provider/modelreg.set_active_provider("my-endpoint")reg.set_active_model("my-model-id")Any OpenAI-compatible endpoint works (vLLM, Together, Groq, Fireworks, …). For non-OpenAI auth schemes, note that LLMProvider.chat() always sends Authorization: Bearer — route through an OpenAI-compatible proxy if the upstream needs a different header.
4.3 Add a platform adapter
Section titled “4.3 Add a platform adapter”Subclass BaseAdapter (kazma-gateway/kazma_gateway/gateway.py:239), implement receive/send, produce IncomingMessage, and register it. For swarm HITL on the new platform, also subclass BusAdapter (kazma_core/swarm/bus.py:66) and wire it in app.py’s bus-singleton block.
4.4 Add a skill
Section titled “4.4 Add a skill”See Skills, MCP & Tools → Adding a custom skill. Sign it with kazma hub sign.
4.5 Add an MCP server
Section titled “4.5 Add an MCP server”See Skills, MCP & Tools → Configuring an MCP server. Tools are discovered at runtime and classified by classify_mcp_tool.
4.6 Add a swarm worker
Section titled “4.6 Add a swarm worker”kazma swarm worker add researcher --model deepseek-chat --provider deepseek --type in_process --role researcherOr via the API:
import httpxhttpx.post("http://127.0.0.1:8000/api/swarm/workers", json={ "name": "researcher", "model": "deepseek-chat", "provider": "deepseek", "worker_type": "in_process", "roles": ["researcher"],})4.7 Tap the V2 memory stack
Section titled “4.7 Tap the V2 memory stack”The V2 Cognitive Engine is the chat default (per-turn recall, tools, auto-store, compaction) and is also used by self-improvement / phonebook. (The V1 UnifiedMemoryAdapter was removed in the V1→V2 cutover.) Custom code:
from kazma_core.memory.recall import recallfrom kazma_core.paths import primary_memory_dbimport sqlite3
conn = sqlite3.connect(primary_memory_db(), check_same_thread=False)conn.row_factory = sqlite3.Row
result = recall("what does the user prefer?", conn=conn, limit=5)# result.beliefs -> list[RecallHit] of currently-valid beliefs# result.episodes -> list[RecallHit] of ranked episodes (FTS5 + dense + PPR, RRF-fused)Writing a belief (functional predicates supersede; set predicates append):
from kazma_core.memory.belief_mutation import mutate_belieffrom kazma_core.paths import primary_memory_db, ops_memory_db
primary = sqlite3.connect(primary_memory_db(), check_same_thread=False)ops = sqlite3.connect(ops_memory_db(), check_same_thread=False)
mutate_belief( primary, "user", "prefers", "dark mode", ops_conn=ops, predicate_type="set", extraction_method="custom", source_session="my-integration",)See Memory & RAG.
5. Telemetry & observability endpoints
Section titled “5. Telemetry & observability endpoints”/api/telemetry/*(telemetry_router) — runtime telemetry./api/dashboard/status— overview for the dashboard.- Swarm metrics at
/api/swarm/workers/\{name\}/metrics.
Prometheus
/metricsdoes not exist. OTel packages are declared but Kazma’s tracing is an in-house span emitter. See Architecture → Observability.
Documentation Audit Notes
Section titled “Documentation Audit Notes”- The WebSocket chat endpoint is dead (410 Gone). All API consumers should use SSE.
- The SSE
approval_requiredevent is the canonical way for frontends to surface HITL pauses; pair it withPOST /api/approve/\{thread_id\}. /api/approveownership enforcement (403 on cross-user) means approval tokens are per-user — an admin can’t approve another user’s task without matching identity fields.- V2 is the single memory stack — per-turn recall, tools, auto-store, and compaction all use
recall()frommemory/recall.py. The V1 4-layer adapter (get_adapter()) was removed in the V1→V2 cutover.