Skip to main content

Overview & Auth

TermFlow runs a local REST + WebSocket API so you can build your own tooling — orchestrators, dashboards, scripts — on top of the same terminals you use by hand. This page covers the base URL, ports, the authentication model, and a few honest caveats before you dive into the per-resource reference.

The two local interfaces

TermFlow exposes two separate localhost services. This section documents the REST + WebSocket API (default port 42031). If you are wiring up an AI agent, you probably want the MCP server instead (port 42032) — see MCP Tools. They are different processes on different ports.

Base URL

http://127.0.0.1:42031/api

All REST endpoints on this page are relative to that base. The two health endpoints (/health and /api/health) sit alongside it and are always reachable. The WebSocket endpoint is ws://127.0.0.1:42031/ws (also aliased at /api/ws).

By default the server binds to 127.0.0.1 (loopback only), so nothing outside your machine can reach it. If you turn on Expose on local network in Settings, it binds to 0.0.0.0 and the host portion of the URL becomes your machine's LAN IP.

Ports

The API and the MCP sidecar each listen on their own port. The defaults differ between a normal installed build and a tauri dev build:

ServiceInstalled app (prod)Dev (tauri dev)What it is
API (REST + WS)4203142051Axum HTTP + WebSocket server
MCP4203242052MCP sidecar (streamable HTTP)
terminal-monitor UI4203042030Companion dashboard (separate app)

This reference uses the prod ports (42031 / 42032) throughout. Both ports are user-overridable in Settings → Connections (valid range 1024–65535). If you have changed them, substitute your own values everywhere below.

Authentication

TermFlow's auth model is deliberately simple, and it is important to understand it correctly.

An honest note: In the default localhost-only mode, every endpoint is unauthenticated. This is not an oversight — it is the design. The server is bound to 127.0.0.1, so only processes on your own machine can reach it, and those already run as you. The bearer token only starts being enforced once you deliberately expose the server on your LAN.

The rule

  • The credential is a bearer token (network.authToken) — a 64-character hex string generated on first run and stored in TermFlow's config file.
  • The token is enforced only when the server is exposed on the LAN (Settings → Connections → Expose on local network). In the default localhost-only mode, requests need no token at all.
  • GET /health and GET /api/health are always exempt — they never require a token, in either mode.
  • The WebSocket endpoint authenticates via a ?token= query parameter (browsers cannot set custom headers on a WebSocket handshake), not the Authorization header.

Where the token lives

Open Settings → Connections. The auth token is shown there (masked, with reveal / copy / Rotate controls). Rotating it takes effect immediately with no server restart, and existing UI connections survive the rotation. This is the value you put in your Authorization header when the server is exposed.

warning
Use the config token, not /api/auth/token

The real credential is the authToken from Settings → Connections. There is a POST /api/auth/token endpoint that mints a JWT, but the auth gate does not check that JWT — it exists only as monitor-compatibility scaffolding. Do not use it to authenticate your API calls; it will not do what you expect.

Examples

On localhost (the default), no auth is needed:

# Loopback, default mode — no token required
curl http://127.0.0.1:42031/api/terminals

When the server is exposed on your LAN, send the config token as a bearer:

# Reached from another machine — token required
curl -H "Authorization: Bearer <authToken>" \
http://192.168.1.20:42031/api/terminals

Health checks never need a token:

GET /api/health HTTP/1.1
Host: 127.0.0.1:42031
{ "status": "ok", "app": "TermFlow", "instanceId": "..." }

For the WebSocket, append the token as a query parameter:

ws://127.0.0.1:42031/ws?token=<authToken>

A behavior to watch for: 200-with-error-body

A small number of endpoints return HTTP 200 OK even when the target terminal does not exist, placing an error in the response body instead of using a 4xx status. This is intentional back-compat behavior. The affected endpoints are:

  • POST /api/terminals/:id/input
  • POST /api/terminals/:id/size
  • POST /api/terminals/:id/resize

For these, check the response body, not just the status code. A missing terminal comes back as:

{ "error": "..." }

with a 200 status. Everywhere else in the API, a non-2xx status means what you would expect.

Resource map

The rest of this reference is split by resource. Every route below is gated only when the server is exposed on the LAN (see Authentication above).

Reference pageCovers
TerminalsCreate, list, inspect, resize, and close terminals; read output and snapshots
Execute & batchRun commands in one terminal or fan a command out to many
ProfilesList, create, update, and delete shell profiles
RecordingsStart / stop recordings and export as JSON, text, HTML, or asciinema
SearchSearch terminal history and manage the search index
System & processesSystem info, per-terminal foreground process and agent detection, tmux status
WebSocketLive output stream and client-to-server input frames
MCP ToolsThe 7 MCP tools agents use (separate MCP server, port 42032)

What this reference intentionally does not document

To keep this reference honest, some routes that exist in the running server are deliberately left out because they are internal, non-functional, or not something you should build on:

  • POST /api/auth/token — mints a JWT the auth gate ignores (see the warning above). Not a real authentication path.
  • /api/test/* (start / stop / capture-backend / capture-frontend / compare / list) — an internal test harness. Not part of the public API.
  • Stub / no-op endpoints — these respond, but do nothing useful, and should be treated as not yet implemented:
    • POST /api/profiles/:id/default — no-op (does not change the default profile).
    • POST /api/terminals/:id/reset — returns a mock response.
    • GET /api/system/metrics — returns all zeros.
    • GET /api/search/history — always returns [].

If a route is not listed on one of the resource pages above, assume it is not part of the supported surface.

Next steps

  • Local API & auth — the concepts behind this page, in more depth.
  • Terminals — the first resource most integrations touch.