Recording
Run a goal and get the video of it
A synchronous run with recording and on-screen annotation forced on, asset-blocking off and humanized timing — and a videoId you can stream back afterwards.
record_run
What it is
The job: Show somebody exactly what the agent did, frame by frame.
record_run is run_goal with four decisions already made: record on, annotate on (a visible cursor travelling the page and highlights on the elements it touches), asset-blocking off so the recording keeps its visuals, and humanized timing so what you are watching is the behaviour a real run has rather than a teleporting cursor.
That combination is why it is its own tool rather than a flag. Recording with assets blocked produces a video of a page with no images in it; recording without annotation produces a video where you cannot tell what was clicked. Getting all four right by hand every time is exactly the kind of thing a tool should do for you.
The run returns a videoId. Stream it back with GET /api/v1/runs/{id}/video using your API key — the recording is lazily persisted to durable storage on first view, and video size is tiered by plan. Recording is opt-in everywhere on the platform and never implicit.
The call
Call it exactly like this.
Copied from the tool's registration and the route handler — not paraphrased.
// MCP tool call — server "twin-browser"
{
"tool": "record_run",
"arguments": {
"target": "https://app.example.com",
"goal": "login"
}
}curl -X POST https://twin-browser.com/api/v1/run \
-H "Authorization: Bearer $TWIN_API_KEY" \
-H "content-type: application/json" \
-d '{"target":"https://app.example.com","goal":"login","record":true,"annotate":true,"blockAssets":false}'| Parameter | Type | What it does |
|---|---|---|
| target* | string | Base URL of the target you are authorized to act on. |
| goal* | string | A registry goal name: login, search, extract, reply or like. |
| proxy | string | Your own egress proxy URL. |
Returns
{
"success": true, "steps": 7, "path": [ … ],
"videoId": "…", "videoUrl": "https://twin-browser.com/api/v1/runs/…/video",
"runId": "…", "credits_charged": 10
}What it costs
10 credits
The same floor as any run, settled higher-of against metered model, compute and egress. Recording keeps assets loaded, so the egress half of that settle is genuinely higher than an asset-blocked run — a recorded run can bill above the floor where the same run without video would not. Storage is capped and tiered by plan.
See the full rate cardWhich one
When a different tool is the right call.
The honest answer is often the neighbouring tool. These are the trades.
run_goalThe same execution. record_run just refuses to let you record badly. If you do not want the video, do not pay the egress — run_goal with asset-blocking on is cheaper.
run_goalrecord_jobIdentical, asynchronous. Long flows you want to watch afterwards belong there; the video is what you came for, so blocking on it is rarely the point.
record_jobscreenshotOne frame versus the whole session, and no run at all. If the question is “what does this page look like”, do not record a run to answer it.
screenshotQuestions
record_run, answered.
- Is every run recorded?
- No. Recording is opt-in and always has been — it costs storage and egress, and a recording of a run nobody will watch is pure waste. record_run and record_job are the two calls that turn it on, plus record: true on a plain run.
- Does the recording end where the run ends?
- Yes — a recorded run holds a video tail so the last frame is the run’s end state rather than the frame before the final click landed, and the file is only published once it has stopped growing. A video that ends one action early is a video that misrepresents the run.
Keep going