Motivation
Everything the SPA does, it does over the same HTTP API you can call yourself. This page is the contract reference: the routes, their auth, and the request/response shapes for the load-bearing endpoints. For exact validation rules, theFormRequest/validate() in each controller is the source of truth;
this page is the navigable map.
Conventions
- Auth. Most routes require
auth:sanctum(SPA cookie or a personal access token). Admin routes additionally carry arole:orcan:gate. The embeddable widget authenticates with awidget.key(public key + minted session token). A guest hitting an authed route gets401; an authenticated caller without the required role gets403. - Tenancy. Every request resolves a tenant; queries are scoped to it. See security & threat model.
- Failures are loud. Endpoints map failure to the correct status (
404missing,422validation,429throttle,5xxdownstream) — never200with an empty body.
Authentication — /api/auth
POST /api/auth/token is the stateless Bearer-token flow for non-browser
clients (the Tauri desktop client). It verifies credentials without a session
or CSRF and returns 201 { token, token_type: "Bearer", user }. The minted
Sanctum personal access token is scoped to least-privilege abilities
(kb:read + kb:chat) and carries a finite 30-day expiry — never ['*'],
never immortal. Wrong/unknown credentials → 422. Request fields: email
(required), password (required), device_name (optional, max 120). See
Desktop client & token authentication.
KB chat & search — /api/kb
The
token.ability:<ability> gate (EnforceTokenAbility middleware) constrains
only Bearer personal access tokens: a PAT not scoped for the action gets
403 { "error": "token_ability_forbidden" }. It is a no-op for cookie-SPA
sessions (which carry a TransientToken, governed by the route’s own gates —
the /api/kb/* group is auth:sanctum + tenant.authorize plus AccessScope /
project-membership scoping, not Spatie RBAC), so these dual-auth routes serve
both transports without breaking either.
POST /api/kb/chat — request: question (string, required, max 10000),
anonymous (bool, optional), a legacy top-level project_key (string, optional),
and a filters object (optional) whose dimensions include filters.project_keys,
filters.tag_slugs, filters.source_types, filters.canonical_types,
filters.connector_types, filters.doc_ids, filters.collection_id,
filters.folder_globs, filters.date_from/date_to. There is no top-level
project_keys — project scoping is filters.project_keys (or legacy
project_key). Response:
{ answer, citations[], confidence, refusal_reason, meta{ provider, model, chunks_used, primary_count, …, latency_ms, latency_ms_breakdown, filters_selected } }.
A refusal returns the same shape with confidence: 0 and a machine-readable
refusal_reason sentinel — not an error. See the
anti-hallucination firewall.
Ingestion — /api/kb/ingest
POST /api/kb/ingest (auth:sanctum, batch ≤ 100). Request:
{ queued, failed, documents[] } where each entry is
{ source_path, status: "queued" | "failed", error? }. The status code is
202 when every document queued, or 207 Multi-Status when some disk
writes failed. One IngestDocumentJob is dispatched per queued document. See the
ingestion pipeline.
Promotion pipeline — /api/kb/promotion
Human-gated (ADR 0003). Nothing reaches canonical storage
until a human approves the issued token — promote only validates and pauses.
- suggest —
{ transcript (≤50000), project_key?, existing_slugs? }. - candidates —
{ markdown (≤200000) }→ 200{ valid: true, parsed: {…} }on success, or 422{ valid: false, errors: {…} }on validation failure. - promote — validates the draft (422 on bad/absent frontmatter, 503 when
promotion is disabled), then returns 202 with a paused-flow envelope
containing a nested
approvalobject (single-usetoken,approve_url,approve_path, …). The actual disk write happens only onapprove.
Conversations, presets & notifications
Admin — /api/admin
All admin endpoints are gated. Most read/CRUD surfaces use
role:admin|super-admin; capability-specific surfaces use a can: gate. Every
group is pinned in the authorization matrix.
Embeddable widget — /api/widget
The KITT widget authenticates with a widget.key (throttle:120,1): GET /api/widget/setup, POST /api/widget/session-token, and the session lifecycle
(sessions/start, sessions/{id}/step, /exec-tool, /cancel, /replay).
See KITT widget.
MCP — /mcp/kb
The enterprise-kb MCP server mounts at /mcp/kb (auth:sanctum). See the
MCP server page.
Worked example
CLI reference
The Artisan command surface.
Security & threat model
The authorization matrix behind every gate.