Skip to content

Web API (Express)

Base URL: http://localhost:PORT (default 3001). Static UI from packages/web/client/dist when built.

Authentication

The local server is token-gated (packages/web/server/local_auth.ts). Every request must present the web token via Authorization: Bearer <token>, an x-liminal-token header, or an authToken query param. The token is generated on first run and stored at ~/.liminal/web_token (0600), or set explicitly with AGENT_WEB_TOKEN. The client reads it from GET /api/config, which is exempt only for local requests (loopback IP or localhost / 127.0.0.1 Host). Production UI also receives the token in a <meta name="liminal-web-auth"> tag when Express serves index.html. The Vireon auth callback callback (/api/vireon/auth/callback) is also exempt so the browser sign-in redirect can land.

Bind host defaults to 127.0.0.1 (AGENT_WEB_BIND_HOST); extra CORS origins via AGENT_WEB_CORS_ORIGINS (defaults include https://www.vireondynamics.com for hosted OAuth handoff).

Exempt from web token auth: /api/vireon/auth/callback, POST /api/integrations/oauth/handoff (loopback only).

Session and config

MethodPathPurpose
GET/api/configUI verbosity, approval timeout, persona bootstrap flags, personaUiTheme, personaDisplayLabel, heartbeat flags. Awaits whenSessionReady().
GET/api/statusHarness busy/idle, optional lastTurnEndedAt for client busy reconciliation
POST/api/session/resetNew session / harness reset (mode: soft | hard, optional greet)
POST/api/session/abortAbort the in-progress turn (409 if idle)

Settings

MethodPathPurpose
GET/api/settingsTabs + fields from buildHarnessSettingsApiFields; provider model/base URL summary (no API keys)
PUT/api/settingsPatch harness.env and/or provider model/baseURL into .agent_runtime_prefs.json. 409 if agent is mid-turn

Locked fields: keys set in real process.env cannot be overridden via PUT. PUT also accepts provider.inferenceMode (byok | managed | auto).

Integrations (OAuth)

Hosted OAuth for Google, Microsoft, Azure, GitHub, Slack, Linear, Notion, Xero, and YouTube. Status and connect flows from packages/web/client/settings/IntegrationsPanel.tsx (web) and the desktop Integrations hub.

MethodPathPurpose
GET/api/integrationsSnapshot: all provider accounts + MCP/OpenAPI connections
GET/api/integrations/xero/begin?mode=read_write|read_onlyStart hosted Xero OAuth — returns connectUrl + state; opens vireondynamics.com
POST/api/integrations/oauth/handoffReceives token bundle from hosted callback (loopback only, auth-exempt)
DELETE/api/integrations/xero?revoke=0|1Disconnect Xero; revoke=1 also revokes tokens at Xero

Other providers use the same hosted /connect/<provider> flow documented in Connectors.

See Xero for operator setup on vireondynamics.com.

Vireon account, licensing & managed inference

Browser sign-in, license/account state, and managed-inference usage. See Accounts & licensing and Managed inference.

MethodPathPurpose
GET/api/vireon/accountConnected account, tier, entitlements, licensed flag
GET/api/vireon/connect/beginStart loopback sign-in — returns connectUrl + state
GET/POST/api/vireon/auth/callbackReceives the signed license token (auth-exempt)
POST/api/vireon/logoutClear local license + account
POST/api/vireon/reconnectRe-apply provider/inference config after sign-in (409 if busy)
GET/api/vireon/inference-statusManaged-inference wallet: remaining/cap/used credits, period end

Multi-chat

Each chat has its own workspace, memory, and session. Endpoints resolve the active chat; switch with activate. See packages/web/server/chatManager.ts.

MethodPathPurpose
GET/api/chatsList chats + orphan ids + active/resident ids
POST/api/chatsCreate a chat (workspaceMode: scratch | folder | reuse)
GET/api/chats/:idChat metadata
POST/api/chats/:id/activateMake a chat the active SSE target
DELETE/api/chats/:idDelete a chat (returns newActiveId)
GET/api/workspace/currentActive chat's workspace root + fingerprint

Audio: transcription & TTS

See Voice. Audio is sent as base64 data URLs (no multer dependency).

MethodPathPurpose
POST/api/audio/uploadPersist an audio clip under the chat → returns attachmentId
POST/api/transcribeRun ASR on a previously uploaded clip
POST/api/ttsSynthesize speech (segmented); returns clip ids + cost
GET/api/tts/clip/:clipIdFetch a synthesized clip's audio bytes

Messaging and approvals

MethodPathPurpose
POST/api/messageUser message + optional file attachments (images and non-image files as data URLs). Awaits session ready.
POST/api/approveTool approval decision
POST/api/answerask_user response
POST/api/persona/bootstrapPersona bootstrap completion payload. Awaits session ready.

SSE

MethodPathPurpose
GET/api/streamServer-Sent Events: text, tool_*, turn_end, harness_running, etc.

Client: packages/web/client/useSSE.ts. Reconnect via Last-Event-ID; buffer while disconnected.

Busy state: Client tracks expected turn_end, polls /api/status, uses lastTurnEndedAt before clearing “processing” after missed events. See UI streaming.

Implementation

  • Routes: packages/web/server/routes.ts
  • Bridge: packages/web/server/agentBridge.ts
  • SSE: packages/web/server/sse.ts

The web UI is the supported client for chat, approvals, and SSE streaming on desktop and mobile browsers.

Liminal AI · Beta docs (v0.1.2) · Marketing: vireondynamics.com/liminal · Compare: vireondynamics.com/liminal/compare