Skip to main content
POST/api/public/v1/sessions/acquire

sessions.acquire

Hold a profile's browser for your app — by profile_id, or by app_id + external_account_id through a binding: opens it on a fitting machine (or reuses the warm one) and returns the session with lease.generation. BUSY with holder_kind person, automation or app when someone else holds it; renew before ttl_seconds and detach when done.

Capability: session.startCommand (idempotent)MCP tool: sessions_acquire
View as Markdown

Parameters

NameTypeRequiredDescription
command_idbodystring (uuid)YesClient-generated UUID for this command. Send the same id (and body) again to get the stored receipt instead of a second change; a different body under the same id is CONFLICT.
holder_refbodystringYesYour stable name for this holder (worker, job); renew and detach must send the same value (min length 1, max length 120)
profile_idbodystring (uuid)NoThe profile to hold; or send app_id + external_account_id to use a binding
app_idbodystringNoYour app's id, e.g. fb-poster
external_account_idbodystringNoThe account's id inside your app (no secrets) (min length 1, max length 200)
device_idbodystring (uuid)NoOpen on this machine; default: the least busy online machine of the profile's OS
ttl_secondsbodyintegerNoThe hold lapses unless renewed within this many seconds (min 30, max 600) Default: `120`.
keep_warm_secondsbodyintegerNoSeconds the browser stays open after you let go, so the next acquire is instant (0 = stop as soon as the hold ends) (min 0, max 1800) Default: `120`.

Examples

curl -X POST "https://browlix.com/api/public/v1/sessions/acquire" \
  -H "Authorization: Bearer $BROWLIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command_id":"<uuid>","holder_ref":"<holder_ref>"}'

Response

201 with { data, replayed: false, correlation_id } the first time; 200 with replayed: true when the same command_id and body come again.

FieldTypeAlways presentDescription
dataobjectYes
data.idstring (uuid)Yes
data.lifecycle"queued" | "preflight" | "starting" | "ready" | "quiescing" | "stopping" | "closed" | "failed" | …Yes
data.control"free" | "reserved" | "controlled" | "handoff_pending" | "revoked" | "uncertain"Yes
data.connectivity"connected" | "reconnecting" | "offline" | "unknown"Yes
data.network"unchecked" | "checking" | "healthy" | "unhealthy" | "unknown"Yes
data.sync"clean" | "dirty" | "snapshotting" | "upload_pending" | "committing" | "committed" | "conflict" | "failed"Yes
data.fenceintegerYesIncrements every time the profile is opened; stale reports carry an older fence (min -9007199254740991, max 9007199254740991)
data.revisionintegerYes(min -9007199254740991, max 9007199254740991)
data.headlessbooleanYes
data.profileobjectYes
data.profile.idstring (uuid)Yes
data.profile.os"windows" | "linux"Yes
data.profile.namestringYes
data.profile.numberintegerYes(min -9007199254740991, max 9007199254740991)
data.profile.group_idstring (uuid) | nullYes
data.profile.revisionintegerYes(min -9007199254740991, max 9007199254740991)
data.deviceobjectYes
data.device.idstring (uuid)Yes
data.device.os"windows" | "linux"Yes
data.device.namestringYes
data.device.statestringYes
data.device.freshnessstringYes
data.leaseobjectYesYour app's hold (here `lease` is the app hold, not the machine's)
data.lease.session_idstring (uuid)Yes
data.lease.app_idstringYes
data.lease.binding_idstring (uuid) | nullNo
data.lease.holder_refstringYes
data.lease.generationintegerYesSend with renew and detach; an older generation is FENCED (min -9007199254740991, max 9007199254740991)
data.lease.state"active" | "detached" | "lapsed" | "ended"Yes
data.lease.ttl_secondsintegerYes(min -9007199254740991, max 9007199254740991)
data.lease.keep_warm_secondsintegerYes(min -9007199254740991, max 9007199254740991)
data.lease.acquired_atstring (date-time)Yes
data.lease.expires_atstring (date-time)Yes
data.lease.keep_warm_untilstring (date-time)No
data.lease.ended_reasonstringNo
data.egressobject | nullYes
data.egress.ipstringYes
data.egress.checked_atstring (date-time)Yes
data.processobject | nullYes
data.process.pidintegerYes(min -9007199254740991, max 9007199254740991)
data.runtimeobject | nullYes
data.runtime.channelstringYes
data.runtime.versionstringYes
data.identityobject | nullYesWhat the browser actually reported (user agent, languages, timezone, …)
data.mismatchesarray<object>Yes
data.remoteobject | nullYes
data.proxy_idstring (uuid) | nullYes
data.run_idstring (uuid) | nullYes
data.run_target_idstring (uuid) | nullYes
data.started_bystring | nullYes
data.stopped_bystring | nullYes
data.stop_reasonstring | nullYes
data.reason_codestring | nullYes
data.network_reasonstring | nullYes
data.ready_msinteger | nullYes
data.ready_atstring (date-time) | nullYes
data.closed_atstring (date-time) | nullYes
data.created_atstring (date-time)Yes
data.updated_atstring (date-time)Yes
data.attach_openintegerYes(min -9007199254740991, max 9007199254740991)
data.pending_changesbooleanYes
data.profile_revisionintegerYes(min -9007199254740991, max 9007199254740991)
data.acquired"opened" | "reused" | "renewed"Yesopened a new browser, reused one already open, or renewed your own current hold
correlation_idstring (uuid)Yes
replayedbooleanNotrue when this command_id was already applied and the stored receipt came back

New fields can appear at any time; ignore the ones you do not know.

Errors

CodeHTTPRetryable
UNAUTHENTICATED401No
FORBIDDEN403No
RATE_LIMITED429Yes
VALIDATION_FAILED422No
CONFLICT409No
OUTCOME_UNKNOWN202Yes
BUSY409Yes
DEPENDENCY_UNAVAILABLE503Yes
UNKNOWN500No

Every error code and when to retry

All operations