Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
ICD-API — Chat Switchboard Backend API Contract
Version: 0.28.3 Updated: 2026-03-13 Audience: Frontend developers, integrators, API consumers
Organization principle: Each file is one domain — the complete story. Every endpoint that touches Knowledge Bases is in
knowledge.md, every endpoint that touches Personas is inpersonas.md, etc.
Files
| File | Domain | Endpoints |
|---|---|---|
| auth.md | Auth — login, register, refresh, OIDC, mTLS | 6 |
| channels.md | Channels, messages, completions, participants, folders, presence | ~35 |
| personas.md | Personas, persona groups, avatars, KB bindings, tool grants | ~20 |
| knowledge.md | Knowledge bases, documents, search, channel/persona bindings | ~12 |
| notes.md | Notes, wikilinks, graph, search, folders | ~10 |
| workspaces.md | Workspaces, file ops, git, credentials | ~20 |
| projects.md | Projects, channel/KB/note associations | ~14 |
| memory.md | Memory extraction, review, admin | 7 |
| providers.md | Provider configs, health, capabilities, routing policies | ~25 |
| models.md | Enabled models, user preferences | 4 |
| notifications.md | Notifications, preferences | 7 |
| extensions.md | Extensions, user settings, admin | 8 |
| profile.md | User profile, avatar, password, app settings | 7 |
| teams.md | Teams, members, groups, permissions, grants | ~25 |
| admin.md | Platform admin — users, settings, stats, audit, usage, vault | ~25 |
| utility.md | Health, export, files, storage | ~12 |
| workflows.md | Workflow definitions, stages, instances, assignments, entry | ~20 |
| tasks.md | Task definitions, runs, scheduler, team tasks | ~14 |
| surfaces.md | Surface registry, extension surfaces | 6 |
| websocket.md | WebSocket protocol, events, rooms | — |
| enums.md | All enum values, policies | — |
Conventions
Base URL
{scheme}://{host}{BASE_PATH}/api/v1
BASE_PATH is empty by default. In path-routed K8s deployments (e.g.
/staging/), all routes shift accordingly. The frontend reads BASE_PATH
from a <meta> tag injected by the Go template engine.
Authentication
JWT bearer tokens. Every request to a protected or admin route requires:
Authorization: Bearer {access_token}
Access token: HS256 JWT, 15-minute TTL. Claims: user_id, email,
role, exp, iat, jti.
Refresh token: Opaque, DB-stored, 7-day TTL. Used to obtain new access tokens without re-login.
WebSocket fallback: ?token={access_token} query parameter when
the Authorization header isn't available.
Cookie sync: On every token save/refresh, the frontend writes
sb_token as a cookie. Go template page routes read this cookie via
AuthOrRedirect middleware for server-rendered surfaces.
Auth Modes
| Mode | AUTH_MODE |
How it works |
|---|---|---|
| Builtin | builtin (default) |
Username/password, bcrypt, JWT |
| mTLS | mtls |
Reverse proxy cert headers, auto-provision |
| OIDC | oidc |
Authorization code flow (Keycloak etc.) |
All three modes issue the same JWT after authentication. Downstream middleware is auth-mode-agnostic.
Authorization Tiers
| Tier | Middleware | Who |
|---|---|---|
| Public | none | Anyone (health, login, register, public settings) |
| Session | AuthOrSession() |
JWT user OR anonymous session cookie (workflow visitors) |
| Authenticated | Auth() |
Any logged-in user |
| Permission | RequirePermission(perm) |
User whose groups grant perm |
| Team Admin | RequireTeamAdmin() |
Admin of the specific team |
| Platform Admin | RequireAdmin() |
Users with role = "admin" |
Error Envelope
All errors return:
{ "error": "human-readable message" }
Standard HTTP status codes: 400 (bad request), 401 (not authenticated), 403 (not authorized), 404 (not found), 409 (conflict), 500 (server error), 502 (upstream provider failure).
Response Envelopes
Three patterns. Every endpoint uses exactly one.
List (returns an array) → always wrap in {"data": []}:
{
"data": [...]
}
If paginated, pagination fields sit alongside data:
{
"data": [...],
"page": 1,
"per_page": 50,
"total": 142
}
Query params: ?page=1&per_page=50. Not all list endpoints paginate —
unpaginated lists still use {"data": [...]}.
This is a hard rule: data is the only array wrapper key. No
domain-specific keys (teams, members, configs, etc.) for arrays.
Clients parse every list response identically.
Single object (GET by ID, profile, health) → return the object directly:
{
"id": "uuid",
"name": "...",
...
}
No wrapping. The response is the resource.
Composite (multiple named values, not an array) → named keys:
{
"active": 5,
"pending": 2
}
Used for stats, counts, status endpoints — anything returning multiple named scalars or heterogeneous data. The key names are domain-specific and documented per endpoint.
Empty arrays must serialize as [], never null. Go handlers
must guard nil slices before serialization:
if result == nil {
result = []MyType{}
}
c.JSON(http.StatusOK, gin.H{"data": result})
ID Format
All resource IDs are UUIDv4 strings, generated application-side via
store.NewID() (Go uuid.New()). This ensures compatibility across
both Postgres and SQLite backends.
Timestamps
ISO 8601 with timezone: "2025-06-15T14:30:00Z". Stored as
TIMESTAMPTZ (Postgres) or TEXT (SQLite).
Page Routes (Non-API)
Server-rendered Go template surfaces. Not REST endpoints — return HTML.
| Route | Surface | Description |
|---|---|---|
/login |
Login | Standalone login/register page |
/ |
Chat | Main chat interface |
/chat/:chatID |
Chat | Chat with specific channel loaded |
/editor |
Editor | Workspace file editor |
/editor/:wsId |
Editor | Editor with specific workspace |
/notes |
Notes | Notes interface |
/notes/:noteId |
Notes | Notes with specific note loaded |
/admin |
Admin | Platform administration |
/admin/:section |
Admin | Admin with specific section |
/settings |
Settings | User settings |
/settings/:section |
Settings | Settings with specific section |
/w/:id |
Workflow Landing | Branded workflow entry page |
/w/:id/:slug |
Workflow Landing | Slug-suffixed workflow entry |
/s/:slug |
Extension Surface | Dynamic surface from registry |
All page routes (except /login, /w/) require authentication via
AuthOrRedirect middleware. Admin routes additionally require
RequireAdminPage(). Workflow landing pages use AuthOrSession.