Skip to main content
DRIFTSTACK

Every endpoint, every shape.

The Driftstack API is documented in a standard machine-readable format (an OpenAPI 3.1 spec), generated from the exact same validation rules (Zod schemas) the server itself enforces at runtime. There is no second source of truth — if a route exists, it's in the spec; if it's in the spec, the SDKs already know its exact shapes (typed bindings).

Interactive reference uses Scalar — try requests against your API key directly in the browser.

The living reference is docs.driftstack.dev — this page is a snapshot.

Routes, grouped.

Sessions

  • POST /v1/sessions
  • GET /v1/sessions
  • GET /v1/sessions/:id
  • POST /v1/sessions/:id/navigate
  • POST /v1/sessions/:id/interact
  • POST /v1/sessions/:id/wait
  • GET /v1/sessions/:id/state
  • POST /v1/sessions/:id/capture
  • DELETE /v1/sessions/:id

Agent sessions

  • POST /v1/agent-sessions
  • GET /v1/agent-sessions/:id
  • POST /v1/agent-sessions/:id/message
  • POST /v1/agent-sessions/:id/mode
  • POST /v1/agent-sessions/:id/input-event
  • POST /v1/agent-sessions/:id/takeover
  • POST /v1/agent-sessions/:id/handback
  • POST /v1/agent-sessions/:id/livekit-token
  • GET /v1/agent-sessions/:id/transcript
  • GET /v1/agent-sessions/:id/gui-control-key
  • DELETE /v1/agent-sessions/:id

Recipes

  • POST /v1/recipes

Profiles

  • POST /v1/profiles
  • GET /v1/profiles
  • GET /v1/profiles/:id
  • PATCH /v1/profiles/:id
  • DELETE /v1/profiles/:id

API keys

  • POST /v1/api-keys
  • GET /v1/api-keys
  • POST /v1/api-keys/:id/rotate
  • DELETE /v1/api-keys/:id

Webhooks

  • POST /v1/webhooks
  • GET /v1/webhooks
  • GET /v1/webhooks/:id
  • PATCH /v1/webhooks/:id
  • DELETE /v1/webhooks/:id
  • POST /v1/webhooks/:id/rotate-secret
  • POST /v1/webhooks/:id/test
  • GET /v1/webhooks/:id/deliveries
  • POST /v1/webhook-deliveries/:id/replay

Account

  • GET /v1/account/me
  • GET /v1/account/audit-log
  • GET /v1/account/audit-log/export
  • GET /v1/account/email-preferences
  • PUT /v1/account/email-preferences
  • GET /v1/account/rate-limits
  • GET /v1/account/me/byok-anthropic-key
  • PUT /v1/account/me/byok-anthropic-key
  • DELETE /v1/account/me/byok-anthropic-key
  • POST /v1/account/me/byok-anthropic-key/test
  • GET /v1/account/me/bundled-llm-settings
  • PATCH /v1/account/me/bundled-llm-settings
  • GET /v1/account/me/bundled-llm-status

Team

  • POST /v1/team/invites
  • GET /v1/team/invites
  • POST /v1/team/invites/accept
  • GET /v1/team/members
  • DELETE /v1/team/members/:id

Billing — crypto orders

  • POST /v1/billing/crypto-checkout
  • POST /v1/billing/crypto-checkout/quote
  • GET /v1/billing/crypto-orders
  • GET /v1/billing/crypto-orders/:id
  • PATCH /v1/billing/crypto-orders/:id
  • POST /v1/billing/crypto-orders/:id/cancel
  • GET /v1/billing/crypto-orders/:id/receipt
  • GET /v1/billing/crypto-orders/:id/receipt.txt
  • GET /v1/billing/crypto-orders/:id/receipt.pdf

Status

  • GET /v1/status
  • GET /v1/status/stream
  • GET /v1/status/sla
  • POST /v1/status/subscribe
  • GET /v1/status/subscribe/confirm
  • GET /v1/status/subscribe/unsubscribe

Auth flows

  • POST /v1/auth/signup
  • POST /v1/auth/login
  • POST /v1/auth/logout
  • POST /v1/auth/verify-email
  • POST /v1/auth/magic-link/request
  • POST /v1/auth/magic-link/consume
  • POST /v1/auth/password-reset/request
  • POST /v1/auth/password-reset/confirm
  • POST /v1/auth/refresh

Billing

  • POST /v1/billing/checkout-session
  • POST /v1/billing/portal-session
  • GET /v1/billing

Three flows, four languages.

Most integrations are built on the same three operations: spin up a session, drive it, capture artifacts. Below: each one in cURL, TypeScript, Python, and Go. Copy-paste straight into a quickstart.

1. Create a session

The minimal "hello world" — provision an iPhone 17 Safari session, return its id. Default archetype if you don't pass one.

# cURL
curl -X POST https://api.driftstack.dev/v1/sessions \
  -H "authorization: Bearer $DRIFTSTACK_API_KEY" \
  -H "content-type: application/json" \
  -d '{"archetype":"iphone17_ios18_7_safari26_4"}'
// TypeScript
import { Driftstack } from "@driftstack/sdk";
const client = new Driftstack({ apiKey: process.env.DRIFTSTACK_API_KEY });
const session = await client.sessions.create({
  archetype: "iphone17_ios18_7_safari26_4",
});
console.log(session.id);
# Python
from driftstack import Driftstack
client = Driftstack(api_key=os.environ["DRIFTSTACK_API_KEY"])
session = client.sessions.create({"archetype": "iphone17_ios18_7_safari26_4"})
print(session.id)
// Go
client := driftstack.New(os.Getenv("DRIFTSTACK_API_KEY"))
session, err := client.Sessions.Create(ctx, &driftstack.CreateSessionRequest{
    Archetype: "iphone17_ios18_7_safari26_4",
})
if err != nil { return err }
fmt.Println(session.ID)

2. Drive the session

Navigate, tap, wait. interact handles taps, typing, scrolling, and key presses; wait blocks until a DOM condition is met or a timeout fires.

# cURL
curl -X POST $URL/navigate \
  -d '{"url":"https://example.com"}' ...
curl -X POST $URL/interact \
  -d '{"action":{"kind":"tap","selector":"button.cta"}}' ...
curl -X POST $URL/wait \
  -d '{"condition":{"kind":"selector","selector":"main"}}' ...
// TypeScript
await client.sessions.navigate(session.id, { url: "https://example.com" });
await client.sessions.interact(session.id, {
  action: { kind: "tap", selector: "button.cta" },
});
await client.sessions.wait(session.id, {
  condition: { kind: "selector", selector: "main" },
  timeout_ms: 5000,
});
# Python
client.sessions.navigate(session.id, {"url": "https://example.com"})
client.sessions.interact(session.id, {
    "action": {"kind": "tap", "selector": "button.cta"},
})
client.sessions.wait(session.id, {
    "condition": {"kind": "selector", "selector": "main"},
    "timeout_ms": 5000,
})
// Go
_, err = client.Sessions.Navigate(ctx, session.ID, &driftstack.NavigateRequest{
    URL: "https://example.com",
})
_, err = client.Sessions.Interact(ctx, session.ID, &driftstack.InteractRequest{
    Action: driftstack.NewTapAction("button.cta"),
})

3. Capture a screenshot

capture takes one of three kinds: screenshot, dom_snapshot, or pdf. The response carries the file's contents directly as base64-encoded text (the data field) — nothing is stored server-side. Session video recording is on the roadmap, not live yet.

# cURL
curl -X POST $URL/capture \
  -d '{"kind":"screenshot"}' ...
# response.data is base64 PNG
// TypeScript
const shot = await client.sessions.capture(session.id, {
  kind: "screenshot",
});
fs.writeFileSync("out.png", Buffer.from(shot.data, "base64"));
# Python
shot = client.sessions.capture(session.id, {"kind": "screenshot"})
Path("out.png").write_bytes(base64.b64decode(shot.data))
// Go
shot, _ := client.Sessions.Capture(ctx, session.ID, &driftstack.CaptureRequest{
    Kind: driftstack.CaptureScreenshot,
})
data, _ := base64.StdEncoding.DecodeString(shot.Data)
os.WriteFile("out.png", data, 0644)

What can go wrong, and what to do about it.

Every error follows the web standard for machine-readable errors — RFC 9457 application/problem+json — and carries a stable type URI: a web link that identifies, and explains, the error kind. The SDKs turn these into named error classes; the cURL caller gets the same JSON straight.

Status Type URI When SDK class
400 errors.driftstack.dev/validation-failed Zod schema mismatch on request body. ValidationError
401 errors.driftstack.dev/unauthorized API key missing, malformed, or revoked. AuthError
404 errors.driftstack.dev/not-found Resource id doesn't exist or isn't visible to this key. NotFoundError
409 errors.driftstack.dev/conflict Resource state precludes the operation (e.g. subscription already active). ConflictError
410 errors.driftstack.dev/session-destroyed Session was destroyed; create a new one. SessionDestroyedError
429 errors.driftstack.dev/tier-limit Account hit a tier cap (profile count, api-keys per account). TierLimitError
429 errors.driftstack.dev/rate-limited Per-account rate limit; retry-after header carries seconds. RateLimitError (retryable)
429 errors.driftstack.dev/concurrency-limit Your plan's limit on sessions running at the same time (the tier-bound concurrent-session cap) — end a session or wait for one to finish. ConcurrencyLimitError
500 errors.driftstack.dev/internal Server-side fault. Logged + alerted. DriftstackError (kind: internal) (retryable)
503 errors.driftstack.dev/feature-unavailable Feature disabled at deploy time (e.g. avatar upload without R2 bucket). FeatureUnavailableError (NOT retryable)

SDK consumers can use isRetryable(err) (TypeScript) / equivalent predicates in Python + Go to filter which errors to retry without re-implementing the mapping.

Stable contract. Versioned. Auto-generated.

  • Every endpoint has Zod schemas for request + response. The OpenAPI 3.1 spec is generated from the schemas — there is no second source of truth.
  • Every error case maps to the web standard for machine-readable errors — an RFC 9457 application/problem+json response with a stable type URI (a link that explains the error).
  • Breaking changes ship under a new path version. /v1 stays stable; /v2 would be a new prefix, not a silent shape change.