Drıftstack docs
Docs

Quickstart

This guide takes you from a fresh signup to your first iPhone Safari session from code. An agent session is an iPhone Safari browser running in Driftstack’s cloud that an AI drives for you — your code gives it a task in plain words, and it reports back what it did and what it found.

This guide uses the AI agent, which needs a paid plan. The free trial includes the API, but not the AI agent.

You will need:

  • A Driftstack account (sign up or sign in)
  • A paid plan (the free trial has no AI agent; plans and prices)
  • A ds_live_… customer API key (created in the dashboard under API keys)
  • The id of one of your saved proxies: every agent session goes out through a proxy you chose (find a proxy’s id)
  • A way to pay for the AI: your plan’s monthly AI credits, or your own Anthropic key (who pays for the AI)
  • Node.js 18+, Python 3.10+, or Go 1.22+

1. Get an API key

After signing up and verifying your email:

  1. Open app.driftstack.io/api-keys.
  2. Click Create key, give it a name (e.g. quickstart), and copy the value. The key is shown once.
  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 API keys are scoped to the account that created them and carry the ds_live_ prefix on every plan.

When a free trial ends, or a subscription lapses, the key keeps working, but creating a session returns 402 until the account chooses a plan. We keep your profiles, proxies and settings for 30 days after your trial ends. A paid plan that lapsed keeps them. 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.

2. Install the SDK

Pick the language you want to use. See SDK installation for full details.

TypeScript / Node.js:

npm install @driftstack/sdk

Python:

pip install driftstack-sdk

Go:

go get github.com/driftstackdev/driftstack-go

All three SDKs are published pre-1.0: TypeScript on npm, Python on PyPI, and Go as a tagged module. Keep your package-manager lockfile, Python constraints, or go.sum under version control for reproducible installs.

3. Run your first session

This snippet starts an agent session through your proxy, gives the agent a task, prints its answer, and closes the session. If the task does not finish, the TypeScript version throws, so a script that runs it fails loudly.

TypeScript:

import { randomUUID } from 'node:crypto';
import { Driftstack } from '@driftstack/sdk';

const client = new Driftstack({ apiKey: process.env.DRIFTSTACK_API_KEY! });

const agent = await client.agentSessions.create({
  mode: 'ai',
  proxy_id: process.env.DRIFTSTACK_PROXY_ID!,
});
try {
  // Wait up to two minutes for its browser: `status` is `active` from the start, so read `ready`.
  for (let i = 0; i < 60; i++) {
    const s = await client.agentSessions.get(agent.id);
    if (s.status === 'closed') throw new Error('The session did not start: ' + s.closed_reason);
    if (s.ready !== false) break;
    await new Promise((resolve) => setTimeout(resolve, 2_000));
  }
  const reply = await client.agentSessions.message(
    agent.id,
    'Open https://example.com and tell me the page title.',
    { idempotencyKey: randomUUID() },
  );
  if (reply.kind !== 'plan-executed' || !reply.ok) {
    throw new Error(`The task did not finish (${reply.kind}); read the reply for why.`);
  }
  console.log(reply.answer);
} finally {
  await client.agentSessions.close(agent.id);
}

Python (sync):

import os
import time
import uuid
from driftstack import Driftstack

with Driftstack(api_key=os.environ["DRIFTSTACK_API_KEY"]) as client:
    agent = client.agent_sessions.create(
        {"mode": "ai", "proxy_id": os.environ["DRIFTSTACK_PROXY_ID"]}
    )
    try:
        # Wait up to two minutes for its browser: `status` is "active" from the
        # start, so read `ready`.
        for _ in range(60):
            s = client.agent_sessions.get(agent["id"])
            if s["status"] == "closed":
                raise RuntimeError(f"The session did not start: {s['closed_reason']}")
            if s.get("ready") is not False:
                break
            time.sleep(2)
        reply = client.agent_sessions.message(
            agent["id"],
            "Open https://example.com and tell me the page title.",
            idempotency_key=str(uuid.uuid4()),
        )
        print(reply["kind"], reply.get("answer"))
    finally:
        client.agent_sessions.close(agent["id"])

Go:

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"

    driftstack "github.com/driftstackdev/driftstack-go"
)

func main() {
    client := driftstack.New(os.Getenv("DRIFTSTACK_API_KEY"))
    defer client.Close()

    ctx := context.Background()
    agent, err := client.AgentSessions.Create(ctx, &driftstack.CreateAgentSessionRequest{
        Mode:    "ai",
        ProxyID: os.Getenv("DRIFTSTACK_PROXY_ID"),
    }, nil)
    if err != nil { log.Fatal(err) }
    defer client.AgentSessions.Close(context.Background(), agent.ID)

    // Wait up to two minutes for its browser: Status is "active" from the
    // start, so read IsReady.
    for i := 0; i < 60; i++ {
        s, err := client.AgentSessions.Get(ctx, agent.ID)
        if err != nil { log.Fatal(err) }
        if s.Status == "closed" {
            reason := "unknown"
            if s.ClosedReason != nil { reason = *s.ClosedReason }
            log.Fatal("The session did not start: " + reason)
        }
        if s.IsReady() { break }
        time.Sleep(2 * time.Second)
    }
    reply, err := client.AgentSessions.Message(ctx, agent.ID,
        "Open https://example.com and tell me the page title.",
        &driftstack.MessageOptions{IdempotencyKey: "quickstart-" + time.Now().Format(time.RFC3339Nano)})
    if err != nil { log.Fatal(err) }
    fmt.Println(reply.Kind, reply.Answer)
}

4. What happened

  • agentSessions.create() started an agent session through the proxy you named, and reserved one of your account’s concurrent session slots. Each plan caps how many sessions run at once (see concurrency and pricing). Exceeding the cap returns 429. The problem type ends in concurrency-limit, with current_sessions and limit in the body. A session counts while its status is provisioning or active, so a session an earlier run left open (one that crashed before its close(), say) can make this create fail on a plan whose cap is 1: list your sessions with GET /v1/agent-sessions, close any that you created and is not closed with DELETE /v1/agent-sessions/{id}, and run again. Without a proxy_id the create is refused with 422 proxy-required, and nothing is created.
  • A new session reads status: active at once, but its iPhone Safari browser is still starting. The loop waits for ready: true, and stops if the session closes instead (closed_reason says why). A message sent before then waits, and can be refused with 409 session-not-ready; nothing runs, and you can send it again.
  • agentSessions.message() gave the agent the task. It planned the page loads and taps, ran them, and answered: kind is plan-executed when it ran, with the answer it read back. The Idempotency-Key makes a retried message the same turn rather than a second one.
  • agentSessions.close() ended the session and released the slot. Close every session you start. Do it in a finally block (defer in Go) as here, so a run that fails part-way does not leave one open.

5. Next steps

  • Profile management — persistent profiles let a session resume cookies, storage, and trust signals across runs.
  • Session lifecycle — full lifecycle reference (states, recovery on reconnect).
  • Run AI tasks from your code — give the AI agent a task in plain words and read its answer, with complete TypeScript, Python, Go and curl programs.
  • Agent sessions — let an AI agent drive a session from plain-language instructions. Live transcript stream via Server-Sent Events.
  • Who pays for the AI — the two ways to pay for the AI: your plan’s monthly AI credits, or your own Anthropic key, used as you choose in Settings (automatic, the default, uses your AI credits first) and not charged by Driftstack.
  • Idempotency keys — Idempotency-Key is honoured on agent-session creation and agent-session messages. Send one on the create too, and a retried create returns the same session instead of a second one.
  • Webhook event catalog — push notifications when a bot check pauses a session, a profile save fails, a crypto payment settles, etc. An agent session sends no session.completed or session.failed: read its status and closed_reason to learn it ended.
  • API versioning policy — what changes are additive, what triggers /v2/*, deprecation cycles.

Stuck? Email [email protected]. Include your account ID (acc_…) + the request ID from any error response (returned in the x-request-id header) so we can trace it.