Quickstart (curl)
This page runs your first Driftstack session using only curl — no
SDK, no install step. An agent session is an iPhone Safari browser in the
cloud that an AI drives for you: create it, give it a task in plain words, read the
answer, and close it, all with plain HTTPS calls, so this is the fastest
way to see exactly what the API sends and receives. Live video and session events
are not part of this walkthrough; see the
live video guide and
webhook events for those. If you’d rather start in
TypeScript, Python, or Go, use the SDK quickstart instead.
You will need a Driftstack account on a paid plan — the free trial has no AI
agent (sign up or
sign in) — curl, jq, the id of one of
your saved proxies (every agent session goes out through a proxy you chose;
see Find a proxy’s id), and a way to pay for
the AI: your plan’s monthly AI credits, or your own Anthropic key
(who pays for the AI).
1. Get an API key
- Open app.driftstack.io/api-keys.
- Click Create key, give it a name, and copy the value. The full key is shown once — Driftstack stores only a hash, so if you lose it you revoke it and mint a new one.
- Export it in your shell:
export DRIFTSTACK_API_KEY="ds_live_…"
export DRIFTSTACK_PROXY_ID="<the id of one of your saved proxies>"
Customer keys on every plan start with ds_live_ and authenticate through
an Authorization: Bearer header on every call.
Pick the narrowest scopes that fit the job — a scope is a permission
attached to the key. This page uses read + write: account/state
requests are reads, while create/message/close drive the session. Keep account_owner (full account control) for dashboards,
not for automated jobs. Full list: API key scopes.
2. Check the key works
curl -H "Authorization: Bearer $DRIFTSTACK_API_KEY" \
https://api.driftstack.dev/v1/whoami
{
"account_id": "acc_…",
"api_key_id": "key_…",
"tier": "starter_v3",
"scopes": ["read", "write"]
}
A 200 naming your account and the key’s scopes means the key is
good. A 401 means the key is wrong, malformed, or revoked —
check the dashboard. Once a free trial has ended, or a subscription has
lapsed, the key keeps working but creating a session returns 402 until
the account chooses a plan. After a failed renewal payment, it returns
402 until the payment goes through, or, once the last retry has failed,
until the account chooses a plan. Your plan’s concurrent-session cap is the
concurrency budget the next step draws from.
3. Create a session
curl -sS -X POST \
-H "Authorization: Bearer $DRIFTSTACK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d "{ \"mode\": \"ai\", \"proxy_id\": \"$DRIFTSTACK_PROXY_ID\" }" \
https://api.driftstack.dev/v1/agent-sessions
{
"id": "agt_…",
"account_id": "…",
"status": "active",
"ready": false,
"mode": "ai",
"model": "claude-sonnet-5-5",
"token_budget_total": 100000,
"token_budget_remaining": 100000,
"proxy_id": "…",
"created_at": "2026-07-07T12:00:00.000Z"
}
The 201 response is the agent session (trimmed here; every field is in the
Agent sessions reference). proxy_id is required: a
create without it is refused with 422 proxy-required, and nothing is
created. The Idempotency-Key makes a retried create return this same
session instead of a second one.
status reads active from the moment the session exists, before its
browser has finished starting. ready tells you when it has. Poll every two
seconds until ready is true:
curl -sS -H "Authorization: Bearer $DRIFTSTACK_API_KEY" \
https://api.driftstack.dev/v1/agent-sessions/agt_… | jq '{status, ready}'
If status turns closed first, the session could not start, and
closed_reason says why. If ready is still false after two minutes, go on
to step 4 anyway. A task sent before the session is ready waits, and can be
refused with 409 session-not-ready. Nothing runs then, so send it again.
Every session occupies one concurrent slot until it ends: when you close it,
or when it ends on its own.
Hitting your tier’s cap returns a 429 whose type ends in
concurrency-limit, with current_sessions and limit in the body — see
Concurrency & backpressure. If you get one on your
first create, a session from an earlier try is probably still open: list your
sessions with GET /v1/agent-sessions, close any that you created and whose
status is not closed (step 5), and create again.
4. Give it a task
curl -sS -N --max-time 3000 -X POST \
-H "Authorization: Bearer $DRIFTSTACK_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "user_message": "Open https://example.com and tell me the page title." }' \
https://api.driftstack.dev/v1/agent-sessions/agt_…/message
The response streams while the agent works: progress events as it plans and
runs each step, and last an event: response whose data: line is the
result:
{
"status": 200,
"body": {
"kind": "plan-executed",
"ok": true,
"answer": "The page title is “Example Domain”.",
"results": [{ "kind": "success", "summary": "Opened https://example.com" }]
}
}
kind: "plan-executed" means the agent planned steps and ran them; answer
is what it read back. The message reference
lists every kind and field.
5. Close the session
curl -sS -X DELETE \
-H "Authorization: Bearer $DRIFTSTACK_API_KEY" \
https://api.driftstack.dev/v1/agent-sessions/agt_…
Returns 204 No Content, frees the concurrent slot, and is idempotent —
closing a closed session also returns 204. Always close when you’re done:
on a paid plan an open session holds its slot until it ends, and that can take
hours.
(On the free trial a session is closed for you after 20 minutes, and the
trial includes 2 hours of session time in total.)
Two habits worth starting now
- Know which POSTs are safe to retry.
Idempotency-Keyis honoured on a short list of POSTs — among them agent-session creation, agent-session messages and steps, profile creation and saving a proxy — where a retry with the same key replays the original response. See Idempotency keys for the exact list. - Know what webhooks tell you about an agent session.
POST /v1/webhookssubscribes an HTTPS endpoint of yours to events. An agent session like this one sends nosession.completedorsession.failed: those come only from sessions started withPOST /v1/sessions. It does sendsession.challenge_detected(a bot check paused it) andsession.profile_save_failed. To learn an agent session ended, readstatusandclosed_reasonfromGET /v1/agent-sessions/{id}. See Webhook endpoints.
Next steps
- SDK quickstart — the same flow in TypeScript, Python, or Go.
- Agent sessions reference — every agent-session endpoint and field.
- Run AI tasks from your code — questions, approvals, stops, errors and retries, with a complete curl script.
- Concurrency & backpressure — caps per tier and how to back off.
- Errors — the stable problem types every endpoint returns.
- Rate limits — request-rate buckets, separate from concurrency.
Stuck? Email [email protected]
with your account ID (acc_…) and the x-request-id header from any
error response.