Skip to content
kazma.
Search Kazma Documentation & Site
ع

API & Route Matrix

Primary surfaces of the FastAPI app (kazma_ui). Auth is default-deny for /api/* unless listed open. HITL danger applies to tool execution, not every HTTP route.

MethodPathAuthDescription
GET/healthOpenLiveness
GET/health/liveOpenProcess live
GET/health/readyOpenReadiness (DB ping when configured)
MethodPathAuth scopeDescription
GET/POST/loginPublic pageMulti-mode login (user / secret / OIDC)
POST/api/auth/*VariesLogin, logout, session (see auth.py, saas_api.py)
GET/api/saas/*Admin / operatorTenants, platform users (RBAC)
MethodPathAuthHITLDescription
POST/api/chat/streamSession/secretGraph interrupt on danger toolsSSE chat stream
POST/api/approve/{thread_id}SessionResumes graph HITLApprove danger tool
*Session CRUD under /api/sessions*Session—Thread list / history (session_manager)
MethodPathAuthHITLDescription
*/api/ide/*SessionVia tool registryFiles, apply_patch, codebase search, LSP (GET/POST /lsp), run, git, swarm send (ide_api.py)
GET/ideSession—IDE page
MethodPathAuthDescription
*/api/swarm/*Session (GET) / Admin (everything else)Workers, dispatch, tasks, metrics. One table, kazma_ui/swarm_panel/route_roles.py; refused with 409 swarm_single_operator on an install with platform users
POST/api/swarm/workers/{name}/circuit-breaker/resetAdminClose a worker’s breaker now (the worker card’s Reset, shown while the breaker is open or half-open)
GET/api/swarm/approvals/{id}SessionOne swarm-bus approval request, for the Swarm page’s approval panel (the link on every chat approval card): the tool, its state, the whole call the gate record keeps, the hash the card shows, and who decided. A chat approval’s gate is not found here
POST/api/swarm/approvals/{id}/decideAdmin{"decision": "approve" | "deny"}: answers the request from the web, the same shared decision as the chat buttons (platform: web). 409 not_pending once it was answered or ended
GET/api/swarm/tasks/exportSessionTask history as JSON, or CSV with ?format=csv (newest 1,000; filters type, status, q as the history list). Task History’s Export CSV
GET/api/swarm/tasks/{id}/streamSessionLive task stream (swarm_sse): the task’s events so far, then live ones until it ends. A finished task’s stream always ends with task_completed and the task’s result, rebuilt from the stored task when the server no longer holds its events (after a restart).
GET/swarmSessionSwarm panel page
*/api/replay/*SessionTime travel: threads, snapshots, restore, fork, compare, clear (replay_routes.py)
GET/replaySessionTime Travel panel page
*/api/research/*SessionResearch: list, detail, compare, export, archive, unarchive (research_panel/routes.py). A report a request names is a path inside a research/reports folder; nothing else is read
POST/api/research/sessionsSessionStart deep research session (background pipeline)
GET/api/research/sessionsSessionList durable research sessions
GET/api/research/sessions/{id}SessionSession status / log / report path
GET/api/research/sessions/{id}/streamSessionSSE progress (snapshot / progress / done)
GET/api/research/evalSessionStructural rubric for ?path=research/reports/… or ?session_id=
POST/api/memory/v2/eval/goldenSessionRun golden memory recall cases (pass rate)
GET/researchSessionResearch panel page (start form + live progress)
MethodPathAuthDescription
GET/api/system/statusSessionMemory health board + embedder/layer probes (build_memory_health)
GET/api/memory/graphSessionProperty graph JSON (nodes/edges); optional ?q= filter
GET/api/memory/graph/statsSessionNode/edge counts + backend path
GET/api/memory/graph/searchSessionFTS search over graph nodes (?q=&limit=)
POST/api/memory/graph/clearSessionBi-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.
*/api/system/memory/*SessionBackup / restore / maintenance of memory stores

Active when memory.v2.use_new_stack is true. All routes return shaped JSON on error (never a bare 500); non-numeric params yield a FastAPI 422.

MethodPathAuthDescription
GET/api/memory/v2/healthSessionV2 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/beliefsSessionActive beliefs list. ?q= FTS filter, ?limit= (default 50, clamped 1–200).
GET/api/memory/v2/beliefs/{id}SessionBelief detail + supersede chain.
POST/api/memory/v2/beliefs/{id}/invalidateSessionSoft-invalidate one belief (+ best-effort Neo4j edge delete).
POST/api/memory/v2/beliefs/invalidate-batchSessionSoft-invalidate many ({ "ids": [...] }).
PATCH/api/memory/v2/beliefs/{id}SessionOperator edit of active triple: optional subject, predicate, object, predicate_type. Sets extraction_method=user_explicit; clears embedding if object changes.
GET/api/memory/v2/graphSessionBelief 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/api/memory/v2/entitiesSessionEntity list for /memory ops. Flags: empty, isolated, protected, core (always protected, never unprotectable), is_self, graph_id (self shells → "user"). Query: ?q=, ?empty_only=, ?isolated_only=, ?limit=.
POST/api/memory/v2/entities/{id}/protectSession{ "protected": true|false }: a protected entity cannot be deleted or merged away (the Memory page’s Protect / Unprotect). Core entities refuse false.
POST/api/memory/v2/entities/{id}/renameSessionDisplay 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/mergeSessionMerge source into target (beliefs rewired, aliases union).
POST/api/memory/v2/entities/linkSessionCreate belief edge (subject, predicate, object).
DELETE/api/memory/v2/entities/{id}SessionDelete 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/summarySessionCounts for ops chips (live/invalidated beliefs, empty/isolated entities).
GET/POST/api/memory/v2/hygiene/*SessionPreview + run empty purge / near-dup invalidate / archive.
GET/POST/api/memory/v2/entity-merges*SessionQuarantine merge list + approve/reject.
GET/POST/DELETE/api/memory/v2/graph/groups*SessionView-only groupings (list/create/delete/move/tier). Never mutates beliefs. Create refuses a node that already has a parent (the answer names the move route); Group under on a grouped node moves it, and Tier… sets a grouped node’s tier. Canvas poll uses groups on GET /graph; Ungroup is DELETE …/groups/{id}.
GET/api/memory/v2/graph/exportSessionOn-demand JSON or GraphML (?format=json|graphml).
POST/api/memory/v2/probeSessionRecall dry-run (explain chips).
POST/api/memory/v2/federated-searchSessionMemory + KB labeled search.
POST/api/memory/v2/eval/goldenSessionGolden recall suite.

Page: GET /memory (HTML admin). Guide: Memory & RAG · Memory best path.

MethodPathAuthDescription
*/api/settings*, config exportAdmin/operatorConfigStore-backed settings UI
PUT/api/settings/single, /api/settingsAdminSave one setting, or several in one transaction. Both apply the same value checks (settings_validation): a time zone the scheduler cannot resolve, a retention the sweep cannot read, a server-status event Kazma does not send answer 400 and nothing is written.
GET/api/settings/system/backupAdminThe settings backup (YAML): every stored setting, keys as vault references, no credential and none of Kazma’s own state. Configuration.
GET/api/settings/export?format=yaml|jsonAdminThe same backup as YAML or JSON.
POST/api/settings/system/restore?dry_run=&sections=&expect=AdminBody: a backup (YAML or JSON, at most 10 MB). dry_run=true returns the plan (changed, keys_restored, keys_to_reenter, refused, kept, digest) and writes nothing. Without it the plan is written; with expect (the preview’s digest) a plan that changed since is refused with 409 and the plan as it is now. 400 for a file that is not a backup, 413 over 10 MB, 503 while settings are held in memory (database away). sections=agent,providers restores only those.
GET / POST/api/settings/system/restore/undoAdminGET: what undoing the last restore would put back (available). POST (expect= the GET’s digest): put it back; a setting changed again since and a key the restore brought back stay. 409 when there is nothing to undo.
GET/PUT/api/settings/agent/nonstopSession / AdminNon-Stop & Self-Healing watchdog/failover/ledger settings
*Workspace routes /api/workspaces*SessionWorkspaceStore CRUD
GET/PUT/api/workspace/extra-rootsSessionFolders outside the workspace the agent may use without asking (Workspace page). PUT replaces the list; a new root must be a full path to an existing folder, not a whole drive and not one holding Kazma’s own files (400 says why). A failed read is a 500, never an empty list.
GET/api/notifications/turn-completeSessionThe “notify when a task finishes” switch, the one reader the Web Push sender uses too

Mounted from documents_api.py at /api/documents/*. Thin transport over the shared DocumentIngestionService (no parallel parser path). Tenant, actor and workspace come from the request, never from the body: the tenant from the tenant middleware, the actor from the signed-in principal’s name (operator for the shared secret sent as a header, local for a request without a credential), the workspace from the active workspace row. Clients never supply trusted server paths.

A document belongs to the actor that ingested it; every read, job read and action checks the document’s ACL. A document or job the caller cannot read answers exactly like one that does not exist (404). On a single-operator install these routes, the chat commands and the agent’s tools all act as the install’s operator (documents.principal, since 2026-10-13), so each sees the documents the others ingested; with platform users (multi-user mode) these routes act as each user, and chat and the agent refuse.

Guide: Document Intelligence · Ops: Document processing · Security: Document security.

MethodPathAuthDescription
POST/api/documentsSessionStreamed upload. Body: the file’s bytes; filename in X-Document-Filename (URL-encoded) or ?filename=; optional X-Idempotency-Key and ?force_ocr=1. The declared size, writes switched on, the storage floor, the queue, the tenant’s caps and its rate window are answered before the body is read (see the answers below; the tenant byte quota is checked while the file is stored); at most documents.capacity.max_concurrent_uploads (default 4) uploads are received at once per process (503 uploads_busy). The body is spooled under the storage root and cut off at documents.intake.max_bytes or at its declared length (413).
POST/api/documents/importSessionIngest a file of the active workspace: {"path": …} (relative to the workspace, or absolute inside it); 400 path_outside_workspace otherwise
POST/api/documents/generateSessionGenerate + durably ingest: target_format, payload (an object of at most 1 MiB serialized, 413 above; nesting deeper than 32 is a 400), optional output_name, title
GET/api/documentsSessionThe documents the caller can read, each with its newest job’s state
GET/api/documents/healthSessionParser/OCR/renderer/PDF-mutator readiness + worker status (read-only diagnostic). With KAZMA_DOCUMENT_WORKER_ISOLATION=container the engines are those the worker image reports
POST/api/documents/mergeSessionMerge the PDFs of document_ids (at least two) into a new artifact
GET/api/documents/{document_id}SessionDetail (versions + jobs + artifacts)
GET/api/documents/{document_id}/versionsSessionVersion list
GET/api/documents/{document_id}/contentSessionNormalized content, fenced as untrusted data: ?page= or ?page_start=&page_end=, offset, max_chars (default 20,000, at most 200,000), with a continuation. 409 not_ready before processing finishes
GET/api/documents/{document_id}/artifactsSessionDerived artifacts for a document
GET/api/documents/artifacts/{artifact_id}/downloadSessionDownload an artifact by opaque ID (404 when the caller cannot read it)
POST/api/documents/{document_id}/convertSessionConvert the current version: target_format, optional output_name → a new artifact
GET/api/documents/{document_id}/pdf-infoSessionStructural PDF report
POST/api/documents/{document_id}/splitSessionSplit a page range: start_page, end_page, optional output_name
POST/api/documents/{document_id}/fill-formSessionFill AcroForm fields: fields (object), optional output_name
POST/api/documents/{document_id}/redactSessionPhysically redact terms (list) → a new artifact; the original is unchanged. No confirmation dialog on the API (the page asks first)
POST/api/documents/{document_id}/indexSessionPublish the current version to the Knowledge library library_id
POST/api/documents/{document_id}/unindexSessionRemove it from the library library_id
POST/api/documents/searchSessionSearch a library: library_id, query, top_k (default 5, 1–50); hits come back inside an untrusted-data fence
GET/api/documents/jobs/{job_id}SessionJob status. 404 Job not found for an unknown job and for one whose document the caller cannot read, identically
GET/api/documents/jobs/{job_id}/eventsSessionAppend-only job event history (the same 404 rule)
POST/api/documents/jobs/{job_id}/cancelSessionCooperative cancel (the same 404 rule; 409 for a job that can no longer be cancelled)
POST/api/documents/jobs/{job_id}/retrySessionRe-run a dead_letter or rejected job as a fresh job (409 for any other state). Needs write permission on the document and passes the same admission as an upload (429/503/507)
POST/api/documents/{document_id}/deleteSessionTombstone: the document leaves every Knowledge library first (a library it cannot leave stops the delete, 500 document_unindex_failed, so a retry can finish it), then its metadata is archived; garbage collection reclaims the bytes later. Optional body {"reason": …}
DELETE/api/documents/{document_id}SessionThe same tombstone as POST …/delete (one implementation); the reason goes in ?reason=

Answers shared by these routes. The routes with a JSON body (import, generate, merge, convert, split, fill-form, redact, index, unindex, search, and the optional body of POST /{id}/delete) read at most 2 MiB: a declared length past it is refused before a byte is read and an undeclared one is cut off (413 request_too_large); malformed JSON, a body that is not an object or nesting too deep is a 422 invalid_json. A live document policy that cannot be read or holds an invalid saved value refuses the call with 503 and Retry-After: 30 (document_policy_unavailable). One status per error code on every route (documents_api._STATUS_BY_CODE, since 2026-10-13; the route helpers kept three tables that disagreed): a refusal the server’s state causes is a 503 with Retry-After: 30 wherever it happens — durable writes switched off (document_platform_disabled: reads, PDF info and cancel keep working), a required malware scanner that cannot run (malware_scanner_unavailable, answered by the upload before its body is read), no permitted worker profile (worker_isolation_*). A code the table does not name keeps its route’s default: 400 for intake and lifecycle, 422 for the action routes. Refusals of upload, import and retry carry a code; the 429, 503 and 507 ones also a reason and a Retry-After header:

StatusCodeWhen
413intake_too_large, quota_exceededPast documents.intake.max_bytes (declared or received), or past the tenant byte quota
507storage_fullFree space minus the incoming file would fall below documents.capacity.storage_free_floor_bytes
503queue_full, capacity_unavailable, uploads_busyThe durable queue is at documents.capacity.max_queued_jobs; a capacity measurement (storage, queue, tenant load, admission store) could not be made; too many uploads being received
429tenant_queue_full, tenant_active_full, rate_limitedThe tenant’s queued/active caps or its files/bytes-per-minute window
409idempotency_conflict, intake_in_progress, intake_taken_overThe key was used for another request or by another actor; the same key is still being received; the upload took so long its lease lapsed and another process rolled it back (nothing was saved: send it again)

When a secret is configured, upload, import, generate, merge, convert, split, fill-form, redact, index and search also count against the per-principal HTTP rate limit bucket documents (429 with Retry-After): 10 requests a minute, search allowed up to 30 in the same window; a saved api.rate_limit.documents_per_minute replaces both.

MethodPathAuthDescription
GET/api/documents/ops/metricsSessionContent-free metrics snapshot
GET/api/documents/ops/capacitySessionBackpressure snapshot + degraded_reasons
GET/api/documents/ops/readinessSessionRead-only: storage backends and multi-replica honesty (jobs_multi_replica, metadata_multi_replica; a SQLite backend chosen on purpose is not degraded), policy (configured vs effective, restart_required), malware, worker_isolation (hostile_document_profile, image_code_matches), stranded_intake_reservations, rollout, accepting_durable_writes. status is degraded when a configured backend fell back or degraded_reasons names a problem (worker isolation unavailable, stranded intake reservations, an outdated worker image, malware scanning blocking intake), disabled while durable writes are off
GET/api/documents/ops/retentionSessionLive retention/GC policy view
GET/api/documents/ops/auditAdminKeyset-paged operational audit of the tenant (?document_id=&event_type=&limit=&before_id=). Admin-only: the trail names documents and actors the caller may not be able to read
POST/api/documents/ops/maintenance/dry-runAdminGC dry-run report
POST/api/documents/ops/maintenance/runAdminGC run (the page always dry-runs and confirms first; the route does not require it)

HTML page: GET /documents (session). The TUI Documents tab reads GET /api/documents and GET /api/documents/{document_id}/content from the running server.

MethodPathAuthDescription
GET/api/email/statusSessionActive provider, auth modes, presets summary
GET/api/email/accountsSessionEvery mail account: source main / settings / env, name, type, address, auth (oauth / password / incomplete), calendar, removable
POST/api/email/accountsSessionAdd another account that signs in with a password (alias, type, address, password, hosts); the login is tried first and a failing one is refused (400)
POST/api/email/accounts/{alias}/removeSessionForget an account added in Settings (its tokens and password)
GET/api/email/presetsSessionGmail/Microsoft IMAP/POP host presets
POST/api/email/protocol/connectSessionSave IMAP/POP for gmail|microsoft|generic
POST/api/email/protocol/disconnectSessionClear protocol + OAuth tokens for provider
POST/api/email/gmail/connectSessionGmail app-password → IMAP
POST/api/email/gmail/disconnectSessionClear Gmail creds
POST/api/email/oauth/gmail/clientSessionSave Google OAuth client id/secret
GET/api/email/oauth/gmail/start · start.jsonSessionBrowser OAuth redirect / JSON; ?account=<name> signs in another account under that name
GET/api/email/oauth/gmail/callbackOpen (OAuth)Token exchange; redirects to Settings
POST/api/email/oauth/microsoft/clientSessionSave Azure app id/secret
GET/api/email/oauth/microsoft/start · start.jsonSessionBrowser OAuth; ?account=<name> for another account
GET/api/email/oauth/microsoft/callbackOpen (OAuth)Token exchange
POST/api/email/oauth/microsoft/device/start · …/pollSessionDevice-code fallback; body {"account": "<name>"} for another account
POST/api/email/oauth/microsoft/disconnectSessionClear Microsoft mail: tokens, the sign-in’s address and scopes (Outlook Calendar keeps its own switch)
GET/api/calendar/statusSessionEach calendar’s state and address, and the active provider
GET/api/calendar/oauth/google/start · start.jsonSessionGoogle Calendar sign-in (callback: the Gmail one)
GET/api/calendar/oauth/microsoft/start.jsonSessionOutlook Calendar sign-in for the calendar only (callback: the Microsoft one, purpose=calendar); mail is left as it is
POST/api/calendar/oauth/microsoft/device/startSessionThe same by device code (poll with /api/email/oauth/microsoft/device/poll)
POST/api/calendar/oauth/google/disconnect · …/microsoft/disconnectSessionTurn that calendar off until it is connected from the card again; mail stays connected

Agent mail ops use tools (email_list, …), not these HTTP routes. Guide: Email integration.

MethodPathAuthDescription
GET/xSessionX Studio page (composer + X-only planner).
GET/api/x/statusSessionConfigured?, handle, caps. Never returns secrets.
POST/api/x/previewSessionDry-run ToU policy (evaluate_post). No network, no ledger.
GET/api/x/draftsSessionFlattened save_proposal items for the Studio inbox.
GET/api/x/auditSessionRecent x_audit.db rows (?action=&limit=).
POST/api/x/postSession + CSRFImmediate post (publish_x_post). Operator click is the approval. Optional reply_to_id, proposal_id (stored text wins).
POST/api/x/deleteSession + CSRFDelete a live tweet (delete_x_post). Operator click is the approval.
POST/api/x/credentialsSession + CSRFSave four OAuth 1.0a keys (vaulted) + handle + caps.
POST/api/x/testSession + CSRFGET /2/users/me with stored keys.
POST/api/x/disconnectSession + CSRFDelete keys, disable posting.
GET/api/x/replySessionAuto-reply config + live poller status.
POST/api/x/replySession + CSRFSave auto-reply settings; starts/stops the mentions poller.
POST/api/x/reply/previewSession + CSRFDry-run a draft. Nothing published or recorded.
GET/api/x/reply/conversationsSessionSummon log (parent + mention + draft/reason).
POST/api/x/reply/pollSession + CSRFFetch mentions from X now (ignores stuck cursor).
POST/api/x/reply/approveSession + CSRFPublish a held (or wire-failed) draft.
POST/api/x/reply/denySession + CSRFPark a held draft.
POST/api/x/reply/retrySession + CSRFRe-run a skipped/failed/drafting summon.
POST/api/x/reply/deleteSession + CSRFDelete the posted reply on X (if any) and drop the log row.

Chat tweets still go through x_post (always HITL + proposal_id). Guide: X publisher. Mentions: X auto-reply.

MethodPathAuthDescription
GET/scheduledSessionMixed clock page (cron jobs and X posts).
GET/api/scheduled/tasksSessionEvery cron job (upcoming and finished, each with its status class and the actions the server allows) and the X publication store’s operations. Prompts are display summaries; the address is a destination label.
GET/api/scheduled/cron/{job_id}SessionOne job with its full prompt and version, for the edit dialog.
POST/api/scheduled/cronSession + CSRFBook a job: timing, prompt, optional kind (reminder/task) and capabilities (read/read_write). Caps, length limits and the scheduled rate bucket apply.
PUT/api/scheduled/cron/{job_id}Session + CSRFEdit timing and/or prompt with the expected_version the form read: 200 rescheduled, 202 edit_pending (during a fire), 409 conflict / not_editable.
DELETE/api/scheduled/cron/{job_id}Session + CSRFCancel: 200 cancelled, 202 cancel_pending (during a fire), 409 for a finished job.
POST/api/scheduled/cron/{job_id}/restoreSession + CSRFRun again: book a finished job anew at timing.
POST/api/scheduled/xSession + CSRFBook an X post (book_x_post). Optional reply_to_id, proposal_id. Answers the stored time and state.

X posts on this page are rescheduled and cancelled through POST /api/x/queue/{operation_id}/reschedule and .../cancel (with the revision the page read), the routes X Studio uses. Every mutating route checks the same origin (kazma_ui/same_origin.py). On an install with platform users every route answers 409 scheduled_single_operator. Behaviour: Scheduled reminders and tasks.

X Studio’s All clocks link is this page. The Studio planner itself is X-only.

MethodPathAuthDescription
WebhooksTelegram/Discord/Slack callbacksAdapter secretsInbound messages + HITL buttons
*/api/gateway/*SessionGateway status/control from CLI
MethodPathAuthDescription
WSVoice routesSessionroutes_voice_ws.py
*Chaos routesKAZMA_CHAOS_ENABLEDDev only
Static/, /chat, /settings, /x, /scheduled, …Cookie/sessionHTML pages

Exact route lists evolve with routers mounted in app.py. For extension points see API & Extension Points.

ModeWhen
Shared secret cookie / headerSingle-operator
Opaque web sessionMulti-user default
Platform RBAC (viewer/operator/admin)SaaS APIs
OIDC PKCESSO login
API token / JWTProgrammatic (where enabled)

See Multi-user SaaS and Environment variables.