Launch & run profiles
Before an agent can automate a page, the target profile must be running. This page walks the full lifecycle through the local daemon API — list profiles, create one (with fingerprint and proxy), launch it, poll its status, and stop it — and explains the PROFILE_NOT_RUNNING error you hit when you skip the launch step.
All requests go to the local daemon. The profile-management API lives under http://localhost:7891/api; the automation API (navigate, click, type, snapshot) lives under http://localhost:7891/api/automation/:profileId/<action> and is covered in HTTP API and the API reference.
Response envelope
Section titled “Response envelope”Every daemon endpoint returns the same JSON envelope, so your code can branch on ok before reading data:
{ "ok": true, "data": { } }{ "ok": false, "error": { "code": "PROFILE_NOT_RUNNING", "message": "..." } }Some endpoints return "data": null on success (launch, stop, unlock). Check ok, not the shape of data.
Profile status values
Section titled “Profile status values”A profile reports one runtime status. Automation only works while the profile is running.
| Status | Meaning |
|---|---|
idle | Created but not launched. The browser process is not running. |
launching | Launch accepted; the browser is starting and syncing remote session state. |
running | Browser is up. Automation endpoints work. |
stopping | Stop accepted; cookies and tabs are syncing before the browser closes. |
locked | The profile is open on another device. You cannot launch it here until the lock is released. |
List profiles
Section titled “List profiles”Lists every profile with its current runtime status. No license is required to read the list.
curl -s localhost:7891/api/profiles | jq '.data[] | {id, name, status}'If you are an external agent that was not handed a specific profile ID, list profiles and let the operator pick one. Once you have an ID, keep it in an environment variable for the rest of the flow:
PROFILE=<profile-id>Create a profile
Section titled “Create a profile”Creating a profile requires an active license. The body carries the profile name, proxy, fingerprint, and geolocation. On success the daemon returns 201 with the new profile and status: "idle".
curl -s localhost:7891/api/profiles -X POST \ -H 'Content-Type: application/json' -d '{ "name": "My Profile", "proxy": {"type": "none"}, "fingerprint": { "os": "windows", "userAgent": "Mozilla/5.0 ...", "screenWidth": 1920, "screenHeight": 1080, "language": "en-US", "timezone": "America/New_York", "webrtc": "fake" }, "geolocation": {"enabled": false} }'The webrtc field accepts real, fake, or disabled. To attach a proxy instead of {"type": "none"}, supply the connection details in the proxy object — see Configure proxies. For the full list of fingerprint fields and how they map to the browser, see Fingerprint parameters.
Update a profile
Section titled “Update a profile”All fields are optional on update — send only what you want to change. The daemon returns the updated profile with its current status.
curl -s localhost:7891/api/profiles/$PROFILE -X PATCH \ -H 'Content-Type: application/json' -d '{"name": "New Name", "tags": ["work"]}'Launch a profile
Section titled “Launch a profile”Launch starts the browser process for a profile. It is asynchronous: on success you get 202 Accepted with "data": null and the status moves to launching.
curl -s localhost:7891/api/profiles/$PROFILE/launch -X POSTLaunch enforces two gates before it accepts:
- Active license. No active subscription →
403withNO_SUBSCRIPTION(account never paid) orLICENSE_EXPIRED(subscription lapsed). Plans are paid-only — see Plans & pricing. - A free concurrent slot. Your plan caps how many profiles can run at the same time — Starter 3, Pro 40, Agency 300. If you are already at the cap, launch returns
403 CONCURRENT_LIMIT_REACHED; stop a running profile or upgrade your plan first.
| Error code | HTTP | Meaning |
|---|---|---|
NO_SUBSCRIPTION | 403 | The account has never had a paid plan. |
LICENSE_EXPIRED | 403 | The subscription has lapsed. |
CONCURRENT_LIMIT_REACHED | 403 | All concurrent slots for your plan are in use. |
PROFILE_NOT_FOUND | 404 | No profile with that ID. |
PROFILE_LOCKED | 409 | The profile is open on another device. |
UNAUTHORIZED | 401 | The daemon’s session tokens are missing or expired; sign in via the app. |
Wait until the profile is running
Section titled “Wait until the profile is running”Because launch returns before the browser is ready, poll the status endpoint until it reads running. Do not send automation actions during launching.
curl -s localhost:7891/api/profiles/$PROFILE/status | jq -r .data.statusA simple wait loop:
curl -s localhost:7891/api/profiles/$PROFILE/launch -X POST
until [ "$(curl -s localhost:7891/api/profiles/$PROFILE/status | jq -r .data.status)" = "running" ]; do sleep 1doneCheck status
Section titled “Check status”The status endpoint is the single source of truth for whether a profile is automatable. It needs no license and returns instantly.
curl -s localhost:7891/api/profiles/$PROFILE/status | jq -r .data.status{ "ok": true, "data": { "status": "running" } }Stop a profile
Section titled “Stop a profile”Stopping closes the browser. Before the process exits, the daemon syncs the session — open tabs and any changed cookies are uploaded so the next launch resumes where you left off. On success you get "data": null and the status moves to stopping, then back to idle.
curl -s localhost:7891/api/profiles/$PROFILE/stop -X POSTPROFILE_NOT_RUNNING
Section titled “PROFILE_NOT_RUNNING”If you call an automation endpoint (/api/automation/$PROFILE/navigate, /snapshot, /click, …) against a profile that is not running, the daemon responds with 404 and the error code PROFILE_NOT_RUNNING.
{ "ok": false, "error": { "code": "PROFILE_NOT_RUNNING", "message": "..." } }This is the most common first-run mistake: the profile exists, but its browser was never started (or was already stopped). Cookie endpoints behave the same way — GET/POST/DELETE /api/profiles/$PROFILE/cookies only work while the profile is running.
How to avoid it:
- Launch the profile (
POST /api/profiles/$PROFILE/launch) before any automation call. - Treat the
202from launch as “accepted, not ready”. PollGET /api/profiles/$PROFILE/statusuntil it returnsrunning— automating duringlaunchingcan still fail. - Re-check status if a long-running flow stalls: a browser crash or a stop from another device flips the profile out of
running. - Work strictly inside the profile the operator gave you. Do not launch, stop, or switch to a different profile to “fix” a mismatch — if the specified profile is not running or does not fit the task, stop and ask the operator.
End-to-end lifecycle
Section titled “End-to-end lifecycle”A complete create → launch → automate → stop flow:
# 0. Pin the profile ID (or create one first)PROFILE=<profile-id>
# 1. Launch (async — returns 202)curl -s localhost:7891/api/profiles/$PROFILE/launch -X POST
# 2. Wait until the browser is upuntil [ "$(curl -s localhost:7891/api/profiles/$PROFILE/status | jq -r .data.status)" = "running" ]; do sleep 1done
# 3. Automate (now safe — no PROFILE_NOT_RUNNING)curl -s localhost:7891/api/automation/$PROFILE/navigate -X POST \ -H 'Content-Type: application/json' -d '{"url":"https://example.com"}'
# 4. Stop — syncs cookies & tabs before closingcurl -s localhost:7891/api/profiles/$PROFILE/stop -X POSTNext steps
Section titled “Next steps”- HTTP API — the automation action endpoints once a profile is running.
- API reference — full endpoint and action catalog.
- Best practices — batching, snapshots, and stealth rules.
- Install the agent skill — drop-in instructions for an AI agent.