Sessions
A session is one running browser instance Driftstack manages
for you. This page is the reference for the
/v1/sessions surface; for the 60-second walkthrough
see api-quickstart.
On hosted Driftstack, a create needs the
proxy_id of one of your saved proxies, or a
profile_id whose profile is bound to one (without either:
422 proxy-required, and nothing is created), and the
step-by-step calls on a session (navigate, interact, wait, extract,
state, capture) answer 503 driver-not-integrated and run
nothing. To drive pages on hosted, start an agent session
(POST /v1/agent-sessions with the same
proxy_id) and send your own steps with
POST /v1/agent-sessions/<id>/steps; see
step-by-step operations on the hosted service.
The step calls below run on a self-hosted deployment.
Lifecycle
| State | What it means |
|---|---|
creating | Driftstack is starting the browser. |
ready | Browser ready; the session URL is reachable. |
busy | Session is actively running an automation step. |
destroyed | Normal end-of-life; the session has ended. |
errored | Abnormal termination; the session cannot recover. |
Transitions: creating → ready → busy → destroyed
is the happy path. errored is terminal; the
session cannot recover.
A session also ends on its own: after 30 minutes without activity, or after 4 hours (20 minutes on the free trial). Create a new one to carry on.
POST /v1/sessions
POST /v1/sessions
Authorization: Bearer ds_live_…
Content-Type: application/json
{
"archetype": "iphone15_ios18_7_safari26_5", ← optional, id from GET /v1/archetypes
"purpose": "production_customer", ← optional, default applied server-side
"label": "ticket-JIRA-1234", ← optional, ≤120 chars
"metadata": { "ticket": "JIRA-1234" }, ← optional, surfaced in webhooks
"profile_id": "prof_01HV…", ← optional, binds session to a profile
"behavioral_profile": "regular", ← optional, persona: casual|regular|power_user
"proxy_id": "6f1c2a9e-…" ← a saved proxy; hosted Driftstack needs one, or a profile_id bound to one
}
→ 201 Created
{
"id": "ses_…",
"account_id": "acc_…",
"api_key_id": "key_…",
"status": "ready",
"archetype": "iphone15_ios18_7_safari26_5",
"purpose": "production_customer",
"label": "ticket-JIRA-1234",
"metadata": { "ticket": "JIRA-1234" },
"created_at": "2026-05-12T12:00:00.000Z",
"updated_at": "2026-05-12T12:00:00.000Z",
"last_state_at": null,
"destroyed_at": null
}
The response is a flat session object — no {"session": …}
envelope. Session ids are prefixed ses_. The
create call returns only after the browser is ready, so
an intermediate creating state is not directly
observable. Poll GET /v1/sessions/:id for later
state changes. The session.completed and
session.failed webhooks signal clean or errored
end-of-life, not readiness.
Self-hosted only: on hosted Driftstack the navigate and capture calls
below answer 503 driver-not-integrated; use
navigate and capture steps in an agent
session there.
The create call does not take a target URL. Drive the session
to a URL after it reaches ready with
POST /v1/sessions/<id>/navigate:
POST /v1/sessions/ses_…/navigate
Authorization: Bearer ds_live_…
Content-Type: application/json
{ "url": "https://example.com", "wait_until": "load" } GET /v1/sessions/:id
GET /v1/sessions/ses_…
Authorization: Bearer ds_live_…
→ 200 OK
{
"id": "ses_…",
"account_id": "acc_…",
"api_key_id": "key_…",
"status": "busy",
"archetype": "iphone15_ios18_7_safari26_5",
"purpose": "production_customer",
"label": "ticket-JIRA-1234",
"metadata": { "ticket": "JIRA-1234" },
"created_at": "2026-05-12T12:00:00.000Z",
"updated_at": "2026-05-12T12:00:08.000Z",
"last_state_at": "2026-05-12T12:00:08.000Z",
"destroyed_at": null
} GET /v1/sessions — list
GET /v1/sessions?limit=25
Authorization: Bearer ds_live_…
→ 200 OK
{
"data": [ /* Session objects, newest first */ ],
"has_more": true,
"next_cursor": "eyJ0aWQiOi…" ← null when no more pages
}
Cursor pagination. Pass the previous response's
next_cursor as ?cursor= on the next
request. Default page size is 50; max
100.
POST /v1/sessions/:id/capture
POST /v1/sessions/ses_…/capture
Authorization: Bearer ds_live_…
Content-Type: application/json
{ "kind": "screenshot" }
→ 200 OK
{
"kind": "screenshot",
"data": "<base64-encoded bytes>",
"encoding": "base64",
"byte_size": 184320,
"duration_ms": 412
}
Captures return inline base64 bytes — there is no presigned
URL. kind selects screenshot,
dom_snapshot, or pdf. Decode
data on the client according to
encoding.
DELETE /v1/sessions/:id
DELETE /v1/sessions/ses_…
Authorization: Bearer ds_live_…
→ 204 No Content
Ends the session immediately and transitions it to
destroyed. Idempotent — DELETEing an
already-destroyed session also returns 204.
Concurrent session limits
Each tier caps the number of sessions you can have in the
live states (creating, ready,
busy) at once. See
rate-limits + the
concurrency doc for the per-tier
table. Hitting the cap returns
429 Too Many Requests with the
concurrency-limit RFC 9457 problem type — wait for
an existing session to finish or upgrade your tier.
Webhooks
Subscribe to session.completed +
session.failed instead of polling. See
/docs/webhooks for the delivery
format + signature verification, and the
event-types table for
the complete list of subscribable events.
Choosing a device (archetype)
The archetype field identifies an exact device, iOS,
and Safari combination from the live GET /v1/archetypes
catalog. Saved profiles (profile_id) pin one returned
id for reuse. For a one-shot session, omit the field and the server
picks a randomly chosen iPhone model from those your plan includes
that are open for new profiles, which can differ from session to
session; the session's archetype_source
reads random.