Skip to main content

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, the FormRequest/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 a role: or can: gate. The embeddable widget authenticates with a widget.key (public key + minted session token). A guest hitting an authed route gets 401; an authenticated caller without the required role gets 403.
  • 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 (404 missing, 422 validation, 429 throttle, 5xx downstream) — never 200 with 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:
Response — an envelope { 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 approval object (single-use token, approve_url, approve_path, …). The actual disk write happens only on approve.
See canonical & promotion.

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.