Skip to main content

Terminals

The Terminals endpoints let you list, create, inspect, resize, write to, and read from terminal sessions over TermFlow's local REST API. This is the core surface an orchestrator or agent uses to drive a real shell.

All paths below are relative to the API base http://127.0.0.1:42031/api (the default production port). Every route in this group returns JSON.

Auth on localhost

In the default localhost-only mode, these endpoints are unauthenticated — that is safe precisely because the server is bound to 127.0.0.1 and never leaves your machine. Authentication (a bearer token) is enforced only when you turn on Expose on local network in Settings → Connections. See API overview & auth for the full model.

Endpoint summary

MethodPathPurpose
GET/api/terminalsList all active terminals
POST/api/terminalsCreate a new terminal
GET/api/terminals/:idGet one terminal's metadata
DELETE/api/terminals/:idKill and remove a terminal
GET/api/terminals/:id/sizeRead the current cols/rows
POST/api/terminals/:id/sizeResize (cols/rows)
POST/api/terminals/:id/resizeResize (cols/rows) — alias of POST …/size
POST/api/terminals/:id/inputWrite raw bytes to the PTY
GET/api/terminals/:id/outputRead cleaned scrollback text
GET/api/terminals/:id/snapshotRead a styled screen snapshot
A "not found" can still be HTTP 200

For back-compatibility, POST …/input, POST …/size, and POST …/resize return HTTP 200 with an {"error": …} body when the terminal id does not exist — they do not return 404. Always inspect the response body, not just the status code. (GET …/size and GET …/:id do return 404.)

The terminal object

GET /api/terminals, POST /api/terminals, and GET /api/terminals/:id all return the same shape:

{
"id": "term_a1b2c3",
"processId": "term_a1b2c3",
"name": "Terminal-powershell",
"profile": "powershell",
"status": "running",
"pid": 48213,
"createdAt": 1751600000000,
"mode": "ui",
"tabId": "tb-4f2a1c9d0"
}
FieldMeaning
idThe terminal's unique id — pass it wherever a :id is required.
processIdMirror of id (kept for client compatibility).
nameDisplay name; defaults to Terminal-<profile> when not supplied.
profileThe resolved shell profile id (see Profiles).
statusAlways "running" for a listed terminal.
pidOS process id of the spawned shell.
createdAtCreation timestamp.
modeInternal origin marker.
tabIdThe owning tab id (a tb-… value).

List terminals

GET /api/terminals

Returns every active terminal.

Response

{
"terminals": [
{ "id": "term_a1b2c3", "processId": "term_a1b2c3", "name": "Terminal-powershell",
"profile": "powershell", "status": "running", "pid": 48213,
"createdAt": 1751600000000, "mode": "ui", "tabId": "tb-4f2a1c9d0" }
]
}

Example

curl http://127.0.0.1:42031/api/terminals

Create a terminal

POST /api/terminals

Spawns a shell and opens a corresponding tab/pane in the UI.

Body parameters (all optional)

FieldTypeDefaultNotes
colsinteger80Initial column count.
rowsinteger24Initial row count.
profileIdstringdefault profileShell profile id. profile is accepted as an alias.
namestringTerminal-<profile>Display name for the tab.
cwdstringprofile's cwdWorking directory to start in.
tabIdstringauto-generatedAttach to an existing tab (tb-…) or let TermFlow mint one.
paneIdstringTarget pane when splitting.
directionstringhorizontal (split right) or vertical (split bottom).
Create defaults differ from MCP

Over REST, a new terminal defaults to 80 cols × 24 rows. The MCP create_terminal tool uses 120 × 40 instead. Pass cols/rows explicitly if you need a specific size.

An unknown or placeholder profileId (for example "default") falls back to the system default profile rather than erroring.

Response — the terminal object with status: "running" (HTTP 200). On spawn failure, HTTP 500 with {"error": …}.

Example

curl -X POST http://127.0.0.1:42031/api/terminals \
-H "Content-Type: application/json" \
-d '{ "name": "build", "cols": 120, "rows": 40 }'

Get a terminal

GET /api/terminals/:id

Returns one terminal's metadata, or HTTP 404 with {"error": "Terminal not found"}.

curl http://127.0.0.1:42031/api/terminals/term_a1b2c3

Delete a terminal

DELETE /api/terminals/:id

Kills the shell process tree and removes all associated state (matching the UI's close path).

Response

{ "status": "ok" }

If the id is unknown, the body is {"error": "Terminal not found"}.

curl -X DELETE http://127.0.0.1:42031/api/terminals/term_a1b2c3

Read the size

GET /api/terminals/:id/size

Returns the terminal's current dimensions.

Response

{ "cols": 120, "rows": 40 }

Returns HTTP 404 with {"error": "Terminal not found"} for an unknown id.

curl http://127.0.0.1:42031/api/terminals/term_a1b2c3/size

Resize a terminal

POST /api/terminals/:id/size · POST /api/terminals/:id/resize

Both paths call the same handler — use whichever you prefer.

Body parameters (both required)

FieldTypeNotes
colsintegerNew column count.
rowsintegerNew row count.

Response

{ "status": "ok", "cols": 100, "rows": 30 }

For an unknown id the response is {"error": "Terminal not found"} at HTTP 200 (see the warning above).

Example

POST /api/terminals/term_a1b2c3/resize HTTP/1.1
Host: 127.0.0.1:42031
Content-Type: application/json

{ "cols": 100, "rows": 30 }

An honest note: if you need content to reflow on resize (rewrapping wrapped lines rather than just changing the viewport), that requires a tmux-backed terminal and the separate POST /api/terminals/:id/resize-reflow path. Plain resize adjusts the PTY dimensions only.


Send input

POST /api/terminals/:id/input

Writes raw bytes straight to the PTY, exactly as if typed. Include your own newline (\n or \r) to submit a command; nothing is added for you.

Body parameters

FieldTypeNotes
datastringThe raw text/bytes to write.

Response

{ "status": "ok" }

An unknown id returns {"error": "Terminal not found"} at HTTP 200.

Example

curl -X POST http://127.0.0.1:42031/api/terminals/term_a1b2c3/input \
-H "Content-Type: application/json" \
-d '{ "data": "ls -la\n" }'
Driving an AI CLI instead of a shell

…/input is for raw keystrokes. To send a prompt to an interactive agent CLI (Claude, Gemini, Copilot, …) with the correct submission keystroke handled for you, use POST /api/terminals/:id/execute instead.


Read output

GET /api/terminals/:id/output

Returns the terminal's scrollback rendered to clean text (ANSI escape codes are always stripped).

Query parameters

ParamTypeDefaultNotes
linesinteger50Number of lines to return. last_lines is accepted as an alias.
offsetinteger0Pagination offset. 0 returns the last N lines (most recent); a non-zero offset paginates forward from that line.

Response

FieldMeaning
totalLinesTotal non-empty lines currently rendered.
offsetThe offset that was applied.
rawThe selected page of lines, joined with \n.
{
"totalLines": 128,
"offset": 0,
"raw": "$ ls -la\ntotal 24\ndrwxr-xr-x 5 user staff 160 ..."
}

An unknown or empty terminal returns {"totalLines": 0, "offset": 0, "raw": ""}.

Example

curl "http://127.0.0.1:42031/api/terminals/term_a1b2c3/output?lines=100"

Read a screen snapshot

GET /api/terminals/:id/snapshot

Returns a styled snapshot of the terminal's current visible screen — an escape-sequence string taken from TermFlow's authoritative screen parser. Written into a freshly reset terminal of the same size, it reproduces the screen exactly, including colors and cursor position. This is what a reconnecting client uses to hydrate in sync with a running TUI.

Response

FieldMeaning
snapshotThe styled escape-sequence screen content.
rowsRow count the snapshot was captured at.
colsColumn count the snapshot was captured at.
{ "snapshot": "...", "rows": 40, "cols": 120 }

An unknown id returns {"snapshot": "", "rows": 0, "cols": 0}.

The snapshot is captured at the parser's current size — align a client by calling resize first. Any cols/rows query params are accepted for forward-compatibility but intentionally ignored (no read-side resizing).

curl http://127.0.0.1:42031/api/terminals/term_a1b2c3/snapshot
Live output vs. one-shot reads

…/output and …/snapshot are point-in-time reads. For a continuous live stream of terminal output, subscribe over the WebSocket API.

Next steps