Skip to content
kazma.
ع Star 7 Get Started

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


All endpoints are mounted by KazmaAppBuilder in kazma-ui/kazma_ui/app.py:615-709. Routers:

RouterPrefix/areaSource
health_router/health/*health.py
chat_routerpage routes (/chat, …)chat.py
settings_router/settingssettings.py
skills_routerskillsskills routes
mcp_routerMCPmcp routes
agents_routeragentsagents routes
providers_router/api/providersproviders routes
sse_router/api/chat/*sse_chat.py
telemetry_routertelemetrytelemetry routes
dashboard_router/api/dashboard/*dashboard.py
models_routermodelsmodels routes
workspace_routerworkspaceworkspace routes
swarm_router/api/swarm/*swarm_panel/
monitor_routermonitormonitor routes
metrics_routermetricsmetrics routes

Plus direct routes in routes_direct.py and a conditional Telegram webhook at /api/webhooks/telegram (app.py:365).


MethodPathPurpose
POST/api/chat/streamPrimary chat transport. Body \{message, session_id, model\}. Returns text/event-stream. (sse_chat.py:353)
GET/api/chat/sessionsList sessions. (line 547)
DELETE/api/chat/sessions/\{session_id\}Delete session. (line 555)
GET/api/chat/sessions/\{session_id\}/messagesSession history. (line 561)

Legacy: GET /ws/chat returns 410 Gone (chat.py:4). Do not use.

MethodPathPurpose
GET/api/provider/activeActive provider/model. (line 583)
GET/api/providersList providers. (line 601)
POST/api/provider/switchSwitch active provider/model. (line 607)
MethodPathPurpose
GET/api/pending-approvalsPending HITL approvals. (hitl_approval.py:146)
POST/api/approve/\{thread_id\}Approve/deny a paused tool. Body `{action: “approve"
MethodPathPurpose
GET/api/dashboard/statusDashboard overview. (dashboard.py:177)
GET/api/sessionsSessions list. (line 221)
POST/api/sessions/clear-allClear sessions. (line 330)
MethodPathPurpose
GET/api/swarm/statusSwarm status.
GET/POST/DELETE/api/swarm/workers[/\{name\}]Worker CRUD.
POST/api/swarm/dispatchDispatch a task (all patterns via type).
GET/api/swarm/tasks[/\{id\}]Task list / detail.
POST/api/swarm/tasks/\{id\}/approveApprove pipeline checkpoint. (routes_tasks.py:612)
POST/api/swarm/tasks/\{id\}/rejectReject pipeline checkpoint. (line 657)
GET/api/swarm/workers/\{name\}/metricsWorker metrics.
GET/api/swarm/circuit-breakersBreaker states.

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

MethodPathPurpose
GET/api/memory/v2/healthV2 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/beliefsActive beliefs list. ?q= FTS filter, ?limit= (default 50, clamped 1–200).
POST/api/memory/v2/beliefs/{id}/invalidateSoft-invalidate one belief (+ best-effort Neo4j edge delete).
POST/api/memory/v2/beliefs/invalidate-batchSoft-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/graphBelief 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/exportOn-demand JSON or GraphML (?format=json|graphml).
GET/api/memory/v2/entitiesEntity 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}/renameDisplay 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/mergeMerge source into target (beliefs rewired, aliases union).
POST/api/memory/v2/entities/linkCreate 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/summaryCounts 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/probeRecall dry-run (explain chips).
POST/api/memory/v2/federated-searchMemory + KB labeled search.
POST/api/memory/v2/eval/goldenGolden recall suite.

Legacy graph family (/api/memory/graph*) — the L2-style property-graph endpoints, now V2-backed and tenant-scoped:

MethodPathPurpose
GET/api/memory/graphProperty graph JSON (nodes/edges); optional ?q= filter.
GET/api/memory/graph/statsNode/edge counts + backend path.
GET/api/memory/graph/searchFTS search over graph nodes (?q=&limit=).
POST/api/memory/graph/clearBi-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.
MethodPathPurpose
GET/health/liveLiveness. (health.py:94)
GET/health/readyReadiness. (line 104)
GET/health/detailsDetailed health. (line 148)
GET/api/gateway/statusGateway/adapter status.
MethodPathPurpose
GET/xX Studio page (composer + X-only planner).
GET/api/x/statusConfigured?, handle, caps. Never returns secrets.
POST/api/x/previewDry-run ToU policy. No network, no ledger.
GET/api/x/draftsFlattened save_proposal items.
GET/api/x/auditRecent x_audit.db rows.
POST/api/x/postImmediate post. Operator click is the approval.
POST/api/x/deleteDelete a live tweet. Operator click is the approval.
POST/api/x/credentialsSave four OAuth 1.0a keys (vaulted) + handle + caps.
POST/api/x/testGET /2/users/me with stored keys.
POST/api/x/disconnectDelete keys, disable posting.
POST / PUT / DELETE/api/scheduled/xBook / 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:MeaningKey payload fields
tokenAn LLM streaming chunk.content
tool_callA tool is starting.tool, args
tool_resultA tool finished.tool, result, is_error
approval_requiredA HITL pause surfaced — frontend should call POST /api/approve/\{thread_id\}. (line 199-207)thread_id, tool, args
doneTurn complete.tokens, cost_usd, duration_ms
errorFatal 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.

// 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;
}
}
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())

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.

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_store
from kazma_core.model_registry import get_model_registry
store = get_config_store()
reg = get_model_registry()
# Option A: use the 'custom' preset shape
reg.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/model
reg.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.

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.

See Skills, MCP & Tools → Adding a custom skill. Sign it with kazma hub sign.

See Skills, MCP & Tools → Configuring an MCP server. Tools are discovered at runtime and classified by classify_mcp_tool.

Terminal window
kazma swarm worker add researcher --model deepseek-chat --provider deepseek --type in_process --role researcher

Or via the API:

import httpx
httpx.post("http://127.0.0.1:8000/api/swarm/workers", json={
"name": "researcher",
"model": "deepseek-chat",
"provider": "deepseek",
"worker_type": "in_process",
"roles": ["researcher"],
})

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 recall
from kazma_core.paths import primary_memory_db
import 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_belief
from 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.


  • /api/telemetry/* (telemetry_router) — runtime telemetry.
  • /api/dashboard/status — overview for the dashboard.
  • Swarm metrics at /api/swarm/workers/\{name\}/metrics.

Prometheus /metrics does not exist. OTel packages are declared but Kazma’s tracing is an in-house span emitter. See Architecture → Observability.


  • The WebSocket chat endpoint is dead (410 Gone). All API consumers should use SSE.
  • The SSE approval_required event is the canonical way for frontends to surface HITL pauses; pair it with POST /api/approve/\{thread_id\}.
  • /api/approve ownership 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() from memory/recall.py. The V1 4-layer adapter (get_adapter()) was removed in the V1→V2 cutover.