Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

Start a screen recording to a video file

/v1/leases/{lease}/recording/startPOST
Records the iPhone screen (H.264) and its sound (AAC) from the same USB capture as screenshots, so screenshots and input keep working while it records. The file is written on the daemon's Mac. It stops by itself after maxDurationSeconds, when the lease ends (release, idle timeout, force), or when the daemon stops; the file is complete in each case. One recording per iPhone at a time (recording-active). A locked iPhone fails with device-locked.
Authorizationoptional
bearerAuth?Bearer <token>
Required on the tunnel. An API token of the account, from ghosthand tunnel url --new-token. The unix socket needs no token
Request Bodyapplication/jsonrequired
path *string
Absolute path of a new .mov or .mp4 file on the daemon's Mac. Its folder must exist; the file must not.
Example: "/tmp/demo.mp4"Pattern: ^/.*\.(mov|mp4)$
fps?number
Frame rate cap, 1 up to the iPhone's rate (usually 60). Default no cap.
Min: 1
maxDurationSeconds?number
Stop by itself after this many seconds, 1...3600.
Default: 300Min: 1Max: 3600
audio?boolean
Record the iPhone's sound too.
Default: true
Response
201 · Recording started
path *string
The video file being written
startedAt *string (date-time)
stopsAt *string (date-time)
When it stops by itself (maxDurationSeconds)
fps?number
The frame rate cap, if one was asked
audio *boolean
The recording has a sound track
Default · Error (RFC 9457 problem details). `code` is stable; switch on it, not on `title` or `detail`.
code *"invalid-request" | "unauthorized" | "not-found" | "device-not-found" | "no-device" | "multiple-devices" | "permission-denied" | "bluetooth-unavailable" | "bluetooth-disconnected" | "no-frame" | "device-locked" | "lock-failed" | "pairing-timeout" | "pointer-not-found" | "lock-state-unavailable" | "lockdown-unavailable" | "unlock-failed" | "mute-unconfirmed" | "text-not-found" | "wait-text-timeout" | "cancelled" | "lease-invalid" | "device-leased" | "recording-active" | "no-recording" | "recording-failed" | "daemon-busy" | "tunnel-offline" | "login-required" | "subscription-required" | "unsupported-in-mode" | "xctest-unavailable" | "task-failed" | "internal-error"
Stable error code. Switch on it; title and detail are for people.
title *string
Short summary of the code, the same for every error with this code
status *integer
HTTP status of the error. In stream mode the response status stays 200.
detail *string
What happened and what to do next. Agents should read it.
Request example
curl -X POST "https://ghosthand.dev/macs/studio/v1/leases/42/recording/start" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "path": "/tmp/demo.mp4", "fps": 1, "maxDurationSeconds": 300, "audio": true }'
Response example
{ "path": "/tmp/demo.mp4", "startedAt": "2026-10-07T09:00:00.000Z", "stopsAt": "2026-10-07T09:01:00.000Z", "fps": 30, "audio": true }