Skip to content
Black Friday30% off your first month. See prices →

Reference REST API Sessions

Sessions API

Create, start, stop and control simulator sessions, install apps or builds on a live device, and export session history as CSV.

These endpoints create and control simulator sessions, install apps on a live device, and read session history. Authentication, error shapes and rate limits are covered in the REST API overview.

Sessions represent live simulator/emulator instances. The lifecycle is: create -> start -> use -> stop.

Whose session. A session belongs to the member who started it. Every endpoint below that acts on a session or shows what is on it (start, control, install, launch, uninstall, list installed apps, /agent/tools, presence, and AI test or suite runs against it) answers 403 to other members of your organization. Organization admins may act on any session in the organization. A teammate the owner has sent a collaboration link can act on that session within the link’s role by sending the link’s token (the part after /collab/) in an X-Collab-Token header alongside their own credentials: View only allows screenshots, the UI tree, logs and the installed-apps list; Can control allows everything except opening links. GET /sessions/{id} stays readable to every member, but it leaves out worker_stream_url for anyone who can’t watch the session and tunnel_url for anyone who can’t control it. Only the owner or an admin can stop a session. Sessions started from a share link or an embed key work differently: every member can watch them, only Developers and Admins can control them, and only the link’s or key’s creator (as a Developer or Admin) or an admin can stop one. See Team.

Session lifetime. A session stays up while something holds it (a browser tab, vibeview dev, the MCP server, API calls, or a test run) and someone is using it. Use is an action on the device: /control, /install-app, /install-build, /launch-app, uninstalling an app, the debug endpoints, /agent/tools calls that drive the device (taps, swipes, typing, buttons, opening links, rotating, relaunching the app, and so on), test steps, app reloads from vibeview dev, and speaking into the microphone. POST /sessions/{id}/presence and the /agent/tools reads (ui_tree, screenshot, logs, find_element and wait) only show that a client is there: they keep a session held but are not use. GET /sessions/{id} is neither: it doesn’t hold a session, so a script that only watches a session should poll POST /sessions/{id}/presence instead. A session nobody uses ends after your organization’s idle timeout (Settings › Session defaults; 15 minutes by default, and never less than five minutes for a session with no browser tab open), whatever holds it. A session that was started but never reached by any client is ended a few minutes after it comes up. A session held by vibeview dev (in the foreground or with --detach) or by the MCP server’s dev_start also ends within about 90 seconds once that process stops checking in (it was killed or crashed, or the computer went to sleep), unless a browser tab or a test run is still holding it. From about a minute before an idle end, the session’s responses carry idle_warning (about a minute before the end; see Session Timeout).

Create Session

POST /api/v1/sessions/

Auth: Bearer token required.

Request body:

FieldTypeRequiredDescription
device_typestringYesios, android or roku. Apple TV is ios and Android TV is android, both with device_category: "tv".
device_categorystringNophone, tablet, or tv. Omit for any handheld. A category alone never allocates a foldable; ask for one with model, or with its device_id on Start Session.
specific_device_idstringNoStart on this device (a device_id from List Devices). Create checks that it exists and is free, and fails with 404/409 if not; Start Session then starts on it, unless you pass a different device_id there. It is not reserved in between: if someone else takes it first, Start queues for it.
modelstringNoStart on any free device of this model (a model from List Device Pools). Use with os_version.
os_versionstringNoThe pool’s os_version (for example iOS 26.4), used with model. A bare version (26.4) is accepted when it names exactly one pool value; a value no device has, or one that could mean two, fails with 422 and lists the valid values.
app_idstringNoPublic id of an app to install before the session becomes usable
build_idintNoInstall this exact build of app_id instead of the latest. A build_id that is not a build of app_id, or one sent without app_id, is ignored. A call made with an API token (or through the MCP server) gets a warnings entry that says so and lists the app’s newest builds. On a session that doesn’t need the app, an app_id that matches none of your apps is reported the same way; a Roku session refuses it with 400, and vibeview dev stops with an App not found message.
launch_paramsobjectNoFlat JSON object applied when the pinned app launches. Ignored without a resolvable app_id.

Response: A session object with status: "pending" and no device yet.

Errors:

  • 400 — app_id is an app for another platform than device_type (for example an iOS app on an android session), or specific_device_id is a device of another platform. A TV app (Apple TV, Android TV) on a phone or tablet, or a phone app on a TV, is refused the same way, whether the device is named or asked for with device_category. The message names both.
  • 404 — Specific device not found.
  • 409 — Specific device is not available.
  • 410 — build_id is a build of app_id that has expired under your plan’s build retention. detail.error is build_expired; message and newer_build_id name the app’s newest build that hasn’t expired (see Build retention). The session isn’t created, and the latest build is never installed instead.
  • 422 — model or os_version matches no device (or os_version is ambiguous). The message lists the valid values.

Start Session

Boot the simulator and begin streaming. Must be called after creating a session.

POST /api/v1/sessions/{session_id}/start

Auth: Bearer token required.

Request body (optional):

FieldTypeRequiredDescription
device_idstringNoStart on this specific device: a device_id from List Devices. Overrides the specific_device_id the session was created with.

Without device_id, the session starts on the specific_device_id it was created with, if any. Otherwise any free device matching the session’s device_type and device_category (or its model and os_version, if you created it with them) is used.

Response: A session object with status updated to starting and the device fields filled in.

If no matching device is free, the request returns 202 Accepted and the session is queued:

{
  "status": "queued",
  "queue_entry_id": 42,
  "position": 2,
  "priority": "low",
  "session_id": "uuid-string",
  "queue_reason": "devices_busy"
}

priority is high on paid plans and low on Free. When every device is busy, paid plans’ requests start first, so a Free request’s position can go up.

queue_reason says why the session is waiting, or is null when that can’t be told:

ValueMeaning
devices_busyEvery matching device is in use.
devices_updatingThe matching devices (or the one device you asked for with device_id) are being updated or restarted, or reset after the previous session ended, or a free device is about to be handed to you. A session starts shortly.
plan_limitYour organization is at its plan’s concurrent session limit.
link_limitA share link’s own concurrent session limit is reached (share and embed sessions only).

The session starts automatically when a device frees up. Use the queue endpoints below to leave the queue.

Errors:

  • 400 — Session already started, or device_id is a device of another platform than the session’s.
  • 404 — Session not found, or no device has that device_id.

Leave Device Queue

Remove a queued session start from the device queue. Two equivalent forms are provided; the POST variant exists for navigator.sendBeacon compatibility (leaving the queue on page unload).

DELETE /api/v1/sessions/queue/{queue_entry_id}
POST   /api/v1/sessions/queue/{queue_entry_id}/leave

Auth: Bearer token required. Only the user who queued the entry can remove it.

queue_entry_id is the integer returned in the 202 queued response from Start Session.

Response:

{
  "status": "ok"
}

Errors:

  • 404 — Queue entry not found or already processed.

Get Session

GET /api/v1/sessions/{session_id}

Auth: Bearer token required.

Response: A session object.

Session Object

Every endpoint that returns a session uses this shape. Fields that don’t apply yet (for example the device fields before the session starts) are null.

{
  "id": 1,
  "session_id": "uuid-string",
  "device_type": "ios",
  "device_category": "phone",
  "status": "active",
  "device_id": 42,
  "device_name": "iPhone 16",
  "device_udid": "ABCD1234-...",
  "device_model": "iPhone 16",
  "os_version": "iOS 18.0",
  "user_email": "dev@example.com",
  "origin": "user",
  "worker_stream_url": "wss://...",
  "tunnel_url": null,
  "start_time": "2026-09-25T10:00:00",
  "end_time": null,
  "duration_minutes": null,
  "failure_reason": null,
  "end_reason_code": null,
  "idle_timeout_s": null,
  "warnings": null,
  "pinned_install_dispatched": true,
  "pinned_install_confirmed": true,
  "app_installed_at": null,
  "app_install_pending": false,
  "idle_warning": null,
  "pinned_app_public_id": "my-app-x7k2",
  "pinned_app_name": "My App",
  "pinned_build_id": null,
  "app_sdk_version": "18.0",
  "device_capabilities": { "appearance": true, "textSizeSteps": 12, "...": "..." },
  "posture": null,
  "form_factor": null,
  "display_metrics": { "chrome": "phone13", "scale": 3, "screen": { "width": 390, "height": 844 }, "corners": { "ul": 47.33, "ur": 47.33, "ll": 47.33, "lr": 47.33 } },
  "embed_app_ready": false,
  "embed_app_readiness_revision": 0
}
FieldDescription
idNumeric session id.
session_idSession identifier used in every /sessions/{session_id} path.
device_typeios, android or roku.
device_categoryphone, tablet or tv. null means any handheld.
statuspending (created, not started), starting (a device is booting), active, stopping (being shut down), stopped or failed.
device_idNumeric id of the allocated device.
device_name, device_model, os_versionThe allocated device’s name, model and OS version.
device_udidThe allocated device’s string identifier (its device_id in List Devices).
user_emailWho started the session.
originWhere the session came from: user (the dashboard or API), embed (an embedded player), share (a public app link) or runner (a CI or AI test run). List Sessions and Get Session work this out; the other endpoints report user.
worker_stream_urlAddress of the session’s live video stream. Treat it as opaque.
tunnel_urlSet only for vibeview dev sessions that carry a Metro connection.
start_time, end_time, duration_minutesTiming. end_time and duration_minutes are set once the session ends.
failure_reasonWhy a failed session failed, for example a queue entry that expired before a device became free, or a device that did not start. null otherwise.
end_reason_codeWhy the session ended, once it is stopped or failed; null before that. The same code as the session.ended webhook’s reason_code and the SDK’s session:ended event, for example stopped, stopped_by_admin, inactivity_timeout, no_client, worker_lost, device_crashed, device_turned_off or device_restarted (see Webhooks). Other codes can appear.
idle_timeout_sFor a session ended with end_reason_code inactivity_timeout: how long it had been idle when it ended, in seconds (the idle timeout that applied). null for any other end and while the session is live.
warningsMessages worth showing the user, such as a plan usage warning or an app install failure.
pinned_app_public_id, pinned_app_name, pinned_build_idThe app (and exact build, if one was chosen) the session was created with. pinned_build_id is null for the latest build.
pinned_install_dispatchedtrue when the session’s app was sent to the device for install as part of starting it.
pinned_install_confirmedNullable boolean for installation during startup: true means installation was confirmed; false means an installation failure was reported; null means no acknowledgement is available, including before startup or when no installation was requested.
app_installed_atWhen the app was confirmed on the device, for devices that install it after the session goes live (Roku).
app_install_pendingtrue while the session is live but its app is not on the device yet. Anything that drives a session automatically should wait for this to be false, not just for status: "active". It is only ever true on Roku, where the channel installs after the session goes live.
idle_warningnull, or {"seconds_remaining": 42, "reason": "inactivity"} from about a minute before the idle timeout ends the session. Set by Get Session and POST /sessions/{id}/presence while the session is active; null elsewhere. /agent/tools results carry the same field, set only for the reads (ui_tree, screenshot, logs, find_element, wait) and always null for a tool that drives the device, since that tool is use. Once someone uses the device, it goes back to null.
app_sdk_versionThe platform SDK the session’s app build was compiled with, when known. Returned by Get Session only.
device_capabilitiesWhich device settings controls this device supports. null until the session goes live.
form_factorfoldable for a device whose display folds, otherwise null.
postureFor a foldable: closed, partial or open. Otherwise null.
display_metricsScreen geometry of the allocated device; see List Devices.
embed_app_ready, embed_app_readiness_revisionUsed by embedded players to know when the app is in the foreground.
webrtc_room_id, stream_urlDeprecated: use worker_stream_url. They will be removed in a future API version.

status: "active" and app_build_id alone do not prove installation succeeded: the build identifies the intended pinned or resolved build. Check pinned_install_confirmed together with session state and the selected app/build identity. A true acknowledgement does not establish foreground readiness or track later manual app changes. Roku still uses app_installed_at and app_install_pending for its post-activation installation.


List Sessions

GET /api/v1/sessions/

Auth: Bearer token required.

Query parameters:

ParameterTypeDescription
statusstringFilter by status: pending, starting, active, stopping, stopped, failed. Repeat it to match several (?status=pending&status=starting). Any other value returns 422.
device_typestringFilter by ios, android or roku (Apple TV is ios and Android TV is android)
platformstringFilter by ios (iPhone and iPad), tvos (Apple TV), android (phones and tablets), androidtv or roku. Repeatable. Any other value returns 422.
sourcestringteam (the dashboard, agents and vibeview dev), visitors (share links and embeds) or runs (test runs and CI). Any other value returns 422.
app_idintOnly sessions that started with this app (numeric app id)
user_idintFilter by user ID
time_rangestringOne of last-hour, last-day, last-week, last-month
searchstringPart of a session ID or a user’s email
limitintMax results (default 50, max 200; out of range returns 422)
offsetintPagination offset (default 0)

Response: Array of session objects, newest first, each with these extra fields:

FieldDescription
sourcedashboard, agent, dev_loop, test_run, ci, share_link or embed.
source_labelThe same in words, as the Session history page shows it, for example Share link "Atlas link".
whoThe team member’s email, the API token’s name, Visitor for share-link and embed sessions, or null.
share_link_id, share_link_label, embed_key_id, embed_key_labelThe share link or embed that opened the session.
source_deletedtrue when that share link or embed no longer exists.
app{id, public_id, name, platform, icon_url} of the app the session started with, or null.
build_versionThe build’s version and build number, for example 1.2.0 (45).
run_session, run_public_idWhether a run created the session, and the run that used it (open /tests/runs/{run_public_id}); run_public_id is null unless source is test_run or ci.
token_labelThe name of the API token that started it.
end_reason_labelend_reason_code in words, once the session has ended.
dispatched_at, agent_commanded_at, run_app_opened_atWhen the session got its device, when an agent first drove it, and when a run first opened its app. null when it did not happen.

Run a Test on a Session

Run a saved test, or every test of a group, on a session you already have, for example the one you are driving on the Devices page or from a coding agent. The call returns at once; open run_url, or poll the run, to follow it. To run on devices of their own, use Start a run instead.

POST /api/v1/sessions/{session_id}/runs

Auth: Bearer token required. Developer role or above.

FieldTypeRequiredDescription
test_idstringOne of the twopublic_id of the test to run
group_idstringOne of the twopublic_id of the group to run
start_from_current_screenbooleanNoNo reset before the first test, so it starts where the device is now. Later tests of a group use their normal start state. Default false
stop_on_first_failurebooleanNoFor a group: after a test fails, skip the rest. Default false
modestringNohybrid (Smart), replay (No AI) or ai (AI only)
modelstringNoModel override
providerstringNoProvider override
environmentstringNoName or id of one of the app’s environments

Pass test_id or group_id, not both.

Response:

{
  "kind": "test_run",
  "run_id": 42,
  "run_public_id": "tr_abc123",
  "run_url": "https://vibeview.io/tests/runs/tr_abc123",
  "status": "started"
}

kind is suite_run for a group. Starting a run counts as use of the session, and the run’s steps keep it in use while they run.

Stop Session

Stop the simulator and release the device.

DELETE /api/v1/sessions/{session_id}

Auth: Bearer token required.

Response:

{
  "status": "ok"
}

Export Sessions (CSV)

GET /api/v1/sessions/export/csv

Auth: Bearer token required.

Returns a CSV file download with session history. It takes the same filters as List Sessions (except limit and offset); with none, it exports every session.

The columns are session_id, user_id, device_type, status, start_time, end_time and duration_minutes, then user_email, who, source, source_label, app, app_platform, build_version, device, os_version and end_reason.

On share-link and embed rows, user_id and user_email are the owner of the link or embed, not the visitor; who is Visitor.


Control Session

Send input commands to the running simulator.

POST /api/v1/sessions/{session_id}/control

Auth: Bearer token required.

Request body:

FieldTypeRequiredDescription
actionstringYesOne of: tap, swipe, rotate, shake, button, key, screenshot, touch_start, touch_move, touch_end
xfloatNoX coordinate in pixels of the streamed video (0 - 10000)
yfloatNoY coordinate in pixels of the streamed video (0 - 10000)
x2floatNoEnd X coordinate in pixels (for swipe)
y2floatNoEnd Y coordinate in pixels (for swipe)
durationintNoDuration in milliseconds
rotationintNoRotation angle: 0, 90, 180, 270
buttonstringNoiOS button: HOME, LOCK, SIRI
keycodestringNoAndroid keycode: BACK, HOME, MENU, DPAD_UP, etc.

Response:

{
  "status": "ok"
}

Install App (Upload)

Upload and install an app binary directly into the running session.

POST /api/v1/sessions/{session_id}/install-app

Auth: Bearer token required.

Request: multipart/form-data

FieldTypeRequiredDescription
filefileYesApp binary (.zip/.tar.gz/.tgz iOS simulator bundle, .apk for Android)
launch_after_installbooleanNoLaunch app after install (default: false)

Install Build

Install an app from an existing build record (no file upload needed).

POST /api/v1/sessions/{session_id}/install-build

Auth: Bearer token required.

Request body:

FieldTypeRequiredDescription
build_idintYesID of the build to install
launch_after_installbooleanNoLaunch app after install (default: true)
grant_permissionsbooleanNoAuto-grant permissions (default: true)
paramsobjectNoFlat JSON object applied when the app launches

Errors:

  • 400 — The build is of an app for another platform than the session’s device, or for a TV when the device is a phone or tablet (or the reverse). The message lists your newest builds that run on this device.
  • 404 — Your organization has no such build. The message lists the session’s app’s newest builds (or, for a session with no app, your newest builds that run on this device).
  • 410 — The build has expired under your plan’s build retention: detail.error is build_expired, with message and newer_build_id (see Build retention). Nothing is installed.

Launch App

Launch an already-installed app on the device.

POST /api/v1/sessions/{session_id}/launch-app

Auth: Bearer token required.

Request body:

FieldTypeRequiredDescription
package_namestringYesApp package identifier
activity_namestringNoAndroid activity to launch
paramsobjectNoFlat JSON object applied when the app launches

List Installed Apps

Request the list of installed apps on the device. The result is delivered asynchronously via WebSocket.

GET /api/v1/sessions/{session_id}/apps

Auth: Bearer token required.


Uninstall App

DELETE /api/v1/sessions/{session_id}/apps/{package_name}

Auth: Bearer token required.

Response:

{
  "status": "ok",
  "message": "Uninstalling com.example.myapp"
}

Last updated 31 Oct 2026 Something wrong on this page? Tell us

Search the docs