Record & Export a Session
Capture what happens in a terminal, stop the capture, then export it as an asciinema cast, an HTML page, plain text, or raw JSON — all through TermFlow's local API.
TermFlow's recording feature turns a live terminal into a saved, replayable session. Every recording is driven by a small set of REST endpoints on the local API, so you can start and stop captures by hand with curl, or let a script (or an agent) do it for you. This tutorial walks the full loop: start → work → stop → inspect → export → play back.
You need a running TermFlow instance with the API reachable at http://127.0.0.1:42031 (the default prod port), and at least one open terminal whose id you know. To list terminals or create one, see the Terminals API. On the default localhost binding every endpoint is unauthenticated — that is safe because the server is bound to 127.0.0.1. You only need an Authorization: Bearer <token> header when TermFlow is exposed on your LAN; see Local API & auth.
The recording loop
A recording is tied to a single terminal. You start it, the terminal keeps doing its work, you stop it, and TermFlow persists the result as a JSON recording you can then export or replay.
Step 1 — Pick a terminal to record
Every recording needs a terminal id. Grab one from the list of open terminals:
curl -s http://127.0.0.1:42031/api/terminals
Copy the id of the terminal you want to capture — we'll call it TERMINAL_ID below. (If you don't have one yet, POST /api/terminals creates one; the Terminals API page covers the body fields.)
Step 2 — Start recording
Send the terminal id to the start endpoint. The options object is optional; omit it to accept the defaults.
POST /api/recordings/start HTTP/1.1
Host: 127.0.0.1:42031
Content-Type: application/json
{
"terminal_id": "TERMINAL_ID"
}
curl -s -X POST http://127.0.0.1:42031/api/recordings/start \
-H "Content-Type: application/json" \
-d '{ "terminal_id": "TERMINAL_ID" }'
On success you get HTTP 201 and the new recording id — hold onto it, you'll need it for every later step:
{
"recordingId": "b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"terminalId": "TERMINAL_ID",
"status": "recording"
}
If the terminal id doesn't exist you get 404 { "error": "Terminal not found" }. If that terminal is already being recorded you get 409.
Recording options
Pass any of these inside an options object to change what is captured. All are optional; the defaults are shown.
| Field | Type | Default | What it does |
|---|---|---|---|
include_output | boolean | true | Capture terminal output |
include_input | boolean | true | Capture keystrokes sent to the terminal |
include_resize | boolean | true | Capture terminal resize events |
compression | string | "gzip" | Stored compressed flag ("none" or "gzip") |
auto_stop | boolean | false | Reserved for automatic stop behavior |
The options object is parsed as a whole, so include every field when you send one — a partial object doesn't merge with the defaults:
curl -s -X POST http://127.0.0.1:42031/api/recordings/start \
-H "Content-Type: application/json" \
-d '{
"terminal_id": "TERMINAL_ID",
"options": {
"include_output": true,
"include_input": false,
"include_resize": true,
"compression": "gzip",
"auto_stop": false
}
}'
Step 3 — Do some work
With the recording active, use the terminal normally. It doesn't matter how the terminal is driven — anything the PTY emits is fair game for capture:
- type directly in the terminal window,
- push a command over the API with
POST /api/terminals/TERMINAL_ID/input, - or let a connected agent run commands through the MCP server.
For a quick, self-contained example, send a command straight to the terminal:
curl -s -X POST http://127.0.0.1:42031/api/terminals/TERMINAL_ID/input \
-H "Content-Type: application/json" \
-d '{ "data": "echo hello from TermFlow\r" }'
You can check whether a terminal is currently being recorded at any time:
curl -s http://127.0.0.1:42031/api/recordings/status/TERMINAL_ID
# { "terminalId": "TERMINAL_ID", "isRecording": true, "status": "recording" }
Step 4 — Stop recording
Stop by recording id (not terminal id). This finalizes the recording, stamps its end time, and writes it to disk.
curl -s -X POST http://127.0.0.1:42031/api/recordings/stop/b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f
{
"recordingId": "b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"status": "stopped",
"size": 4096,
"eventCount": 128
}
size is the total captured payload in bytes and eventCount is how many events were recorded.
Step 5 — Inspect the recording
List every saved recording (newest first):
curl -s http://127.0.0.1:42031/api/recordings
{
"recordings": [
{
"id": "b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"terminalId": "TERMINAL_ID",
"startTime": "2026-07-04T10:15:00+00:00",
"endTime": "2026-07-04T10:17:30+00:00",
"size": 4096,
"eventCount": 128
}
]
}
For a single recording's summary — including its duration in milliseconds and metadata (title, shell type, initial size) — use the info endpoint:
curl -s http://127.0.0.1:42031/api/recordings/b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f/info
{
"id": "b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
"terminalId": "TERMINAL_ID",
"startTime": "2026-07-04T10:15:00+00:00",
"endTime": "2026-07-04T10:17:30+00:00",
"eventCount": 128,
"size": 4096,
"compressed": true,
"duration": 150000,
"metadata": {
"title": null,
"shell_type": "bash",
"initial_size": { "cols": 80, "rows": 24 }
}
}
GET /api/recordings/{id} returns the complete recording object, including every captured event.
Step 6 — Export
Export takes the recording id and a format. The response is a file download (the server sets a Content-Disposition attachment header), so redirect it to a file with curl -o.
# Export as an asciinema cast
curl -s -X POST http://127.0.0.1:42031/api/recordings/b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f/export \
-H "Content-Type: application/json" \
-d '{ "format": "asciinema" }' \
-o session.cast
format | Content-Type | Contains |
|---|---|---|
json | application/json | The complete recording: metadata and every event (output, input, resize) |
text | text/plain | A metadata header plus the concatenated output stream |
html | text/html | A standalone dark-themed HTML page showing the output stream |
asciinema | application/json | An asciinema v2 .cast file (one header line + one line per output event) |
Only the json format preserves every event type. The text, html, and asciinema exports include the terminal's output events — input keystrokes and resize events are not written into those three formats.
An unsupported format returns 400 { "error": "Unsupported format" }; an unknown recording id returns 404.
Play back an asciinema cast
The .cast file is standard asciinema v2, so any asciinema player understands it. With the asciinema CLI installed:
asciinema play session.cast
The header carries the terminal's initial width/height (from the recording's metadata.initial_size), the recording's start timestamp, and an env with SHELL/TERM, so playback matches the original geometry.
Play back inside TermFlow
TermFlow also ships a built-in playback viewer that replays a recording in a read-only terminal with a scrubber, speed control (0.25×–4×), and a loop toggle. Keyboard controls in the viewer:
| Key | Action |
|---|---|
Space | Play / pause |
Esc | Stop |
Shift+← | Step to previous |
Shift+→ | Step to next |
Home | Seek to start |
End | Seek to end |
See Session recording for the in-app experience.
Clean up
Delete a recording you no longer need (returns 204 No Content):
curl -s -X DELETE http://127.0.0.1:42031/api/recordings/b0c1e2f3-4a5b-6c7d-8e9f-0a1b2c3d4e5f
Endpoint reference
| Method & path | Purpose |
|---|---|
POST /api/recordings/start | Start recording a terminal (body: terminal_id, options?) |
POST /api/recordings/stop/{id} | Stop and persist a recording (id = recording id) |
GET /api/recordings | List all saved recordings |
GET /api/recordings/{id} | Get a full recording, including events |
GET /api/recordings/{id}/info | Get a recording's summary and metadata |
POST /api/recordings/{id}/export | Export a recording (body: format) |
DELETE /api/recordings/{id} | Delete a recording |
GET /api/recordings/status/{terminalId} | Check whether a terminal is being recorded |
GET /api/recordings/active | List the ids of all active recordings |
An honest note: Session recording is part of TermFlow 0.1.0's early-access release. The Recordings API, all four export formats, and the playback viewer described on this page are implemented and working today; expect the capture and export pipeline to keep evolving as TermFlow moves past pre-release.
Next steps
- Recordings API reference — the full parameter and response reference for every endpoint above.
- Session recording — the in-app recording and playback experience.