Drıftstack docs
Docs

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

  1. Open app.driftstack.io/api-keys.
  2. 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.
  3. 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-Key is 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/webhooks subscribes an HTTPS endpoint of yours to events. An agent session like this one sends no session.completed or session.failed: those come only from sessions started with POST /v1/sessions. It does send session.challenge_detected (a bot check paused it) and session.profile_save_failed. To learn an agent session ended, read status and closed_reason from GET /v1/agent-sessions/{id}. See Webhook endpoints.

Next steps

Stuck? Email [email protected] with your account ID (acc_…) and the x-request-id header from any error response.