API Reference¶
TermBeam exposes a REST API and WebSocket interface.
REST API¶
All API endpoints (except /login, /api/auth, and /api/version) require authentication via cookie or Bearer token.
Note
Bearer authentication accepts the raw password in the Authorization: Bearer <password> header, not a session token.
Authentication¶
POST /api/auth¶
Authenticate and receive a session token.
Request:
Response (200):
Sets an httpOnly cookie pty_token.
Response (401):
Response (429):
Sessions¶
GET /api/sessions¶
List all active sessions.
Response:
[
{
"id": "a1b2c3d4",
"name": "my-project",
"cwd": "/home/user/project",
"shell": "/bin/zsh",
"pid": 12345,
"clients": 1,
"createdAt": "2025-01-01T00:00:00.000Z",
"color": "#4a9eff",
"lastActivity": 1719849600000
}
]
| Field | Type | Description |
|---|---|---|
color |
string | Hex color assigned to the session |
lastActivity |
number | Unix timestamp (ms) of the last PTY output |
POST /api/sessions¶
Create a new session.
Request:
{
"name": "My Session",
"shell": "/bin/bash",
"args": ["-l"],
"cwd": "/home/user",
"initialCommand": "htop",
"color": "#4ade80"
}
All fields are optional. If initialCommand is provided, it will be sent to the shell after startup. If color is omitted, a color is assigned automatically from a built-in palette.
The shell field is validated against the list of detected shells (see GET /api/shells). The cwd field must be an absolute path to an existing directory.
Response (200):
Response (400):
PATCH /api/sessions/:id¶
Update session properties.
Request:
All fields are optional.
Response (200):
Response (404):
GET /api/sessions/:id/detect-port¶
Scan a session's scrollback buffer for the last localhost or 127.0.0.1 URL and return the port number.
Response (200) — port found:
Response (200) — no port found:
Response (404):
DELETE /api/sessions/:id¶
Kill and remove a session.
Response (200):
Response (404):
GET /api/shells¶
List available shells on the host system.
Response:
{
"shells": [
{ "name": "bash", "path": "/bin/bash", "cmd": "/bin/bash" },
{ "name": "zsh", "path": "/bin/zsh", "cmd": "/bin/zsh" }
],
"default": "/bin/zsh",
"cwd": "/home/user"
}
| Field | Type | Description |
|---|---|---|
name |
string | Display name of the shell |
path |
string | Full path to the shell executable |
cmd |
string | Original command name (on Windows this differs from the full path) |
default |
string | Path to the server's default shell |
cwd |
string | Server's default working directory |
---
#### `GET /api/share-token`
Generate a fresh share token for sharing access. The token is single-use and expires after 5 minutes. Requires authentication.
**Response (200):**
```json
{ "url": "https://your-tunnel-url/?ott=<token>" }
The returned URL auto-logs in whoever opens it. The token is consumed on first use and expires after 5 minutes. When accessed through a tunnel, the URL uses the public tunnel address.
Response (404):
Returned when the server was started with --no-password.
Utilities¶
GET /api/version¶
Get the server version.
Response:
GET /api/dirs?q=/path¶
List subdirectories for the folder browser.
Response:
POST /api/upload¶
Upload an image file. The request body is the raw image data with the appropriate Content-Type header (e.g., image/png, image/jpeg).
Request headers:
Content-Type: Must be animage/*type
Response (200):
Response (400):
Returned when the file's magic bytes don't match the declared Content-Type header.
Response (413):
Maximum file size is 10 MB.
GET /uploads/:id¶
Serve a previously uploaded file by its opaque ID. Requires authentication.
Response: The file content with appropriate content type.
Response (404):
Port Preview¶
GET /preview/:port/*¶
Reverse-proxies requests to a service running on 127.0.0.1:<port>. All HTTP methods are supported. The path after the port is forwarded as-is, along with query strings and request headers.
Requires authentication (cookie or Bearer token).
Single-port proxy
The preview proxies one port at a time via HTTP only. It does not proxy WebSocket connections, so live-reload and HMR will not work. Each request is forwarded individually — there is no persistent upstream connection.
Limitations when accessed through a tunnel
- Server-rendered apps (Next.js SSR, Rails, Django) work best — the browser receives complete HTML with no extra fetches.
- Client-side SPAs may break if they make API calls to a different port or use hardcoded
localhostURLs. Apps that use a single point with an internal reverse proxy (e.g., nginx proxying/apito a backend) work fine. - Multi-port architectures (e.g., frontend on port 3000 making API calls to port 4000) won't work unless the app routes all requests through TermBeam's preview proxy (e.g.,
/preview/4000/apiinstead oflocalhost:4000/api). - The upstream service must be listening on
127.0.0.1(localhost) on the machine running TermBeam.
Response: The upstream response is streamed back with its original status code and headers.
Response (400):
Response (502):
Returned when the upstream service is unreachable or the connection is refused.
Response (504):
Returned when the upstream service does not respond within 10 seconds.
WebSocket API¶
Connect to ws://host:port/ws.
Message Types (Client → Server)¶
Authenticate¶
If the server has a password set and the WebSocket connection wasn't authenticated via cookie, send an auth message first.
or with an existing token:
Response:
Auth Failure:
The connection is closed after sending this message. Sending any non-auth message before authenticating also results in this error and connection closure.