TypeScript SDK quickstart
The @driftstack/sdk package wraps the same REST
surface as the curl quickstart,
with end-to-end types generated from the OpenAPI spec. If you're
already in a TS codebase, this is the recommended way to call
Driftstack.
SDK automation needs a ds_live_… customer API key. Every
plan includes the API, the free trial too.
1. Install
# bun
bun add @driftstack/sdk
# npm
npm install @driftstack/sdk
# pnpm
pnpm add @driftstack/sdk
Node ≥ 18 is supported. The package is dual-published (ESM + CommonJS via conditional
exports); both import and
require('@driftstack/sdk') work out of the box.
2. Construct the client
import { Driftstack } from '@driftstack/sdk';
const client = new Driftstack({
apiKey: process.env.DRIFTSTACK_API_KEY!,
// baseUrl defaults to https://api.driftstack.dev; override for
// self-hosted deployments or staging.
}); The constructor does not make any network calls. Reuse one client across your process — it is internally pooled and safe for concurrent use.
3. Run your first agent session
An agent session is an iPhone Safari browser in the cloud that an AI drives for you:
your code sends a task in plain words, and the agent plans the taps and
page loads, runs them, and answers. Every agent session goes out through
one of your saved proxies, so the create names its id
(proxy_id). You will need:
- Any paid Driftstack plan: every paid plan includes the API and the AI agent. The free trial includes the API, but not the AI agent.
-
The id of one of your saved proxies
(find a proxy's id),
exported as
DRIFTSTACK_PROXY_ID. - A way to pay for the AI: your plan's monthly AI credits, or your own Anthropic key (who pays for the AI).
import { randomUUID } from 'node:crypto';
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') console.log(reply.answer ?? reply.results);
else console.log(reply.kind, reply);
} finally {
// Always close it: an open session counts against your plan's
// concurrent-session limit.
await client.agentSessions.close(agent.id);
} 4. What happened
-
agentSessions.create()started an agent session through the proxy you named. Without aproxy_idthe create is refused with422 proxy-required, and nothing is created. -
A new session reads
status: activeat once, while its iPhone Safari browser is still starting. The loop waits forready: true, and stops if the session closes instead. -
agentSessions.message()gave the agent the task.kindisplan-executedwhen it ran, with theanswerit read back. The idempotency key makes a retried message the same turn rather than a second one. -
agentSessions.close()ended the session and released its slot.
The guide Run AI tasks from your code covers the rest a program needs: answering the agent's questions, approving a payment it held, stopping a task that runs too long, safe retries, and limits.
5. Paginate over your agent sessions
for await (const s of client.agentSessions.iterate({ limit: 50 })) {
if (s.status === 'closed') console.log(s.id, s.closed_reason);
} iterate walks cursor pages transparently, newest first, and
stops when the server returns next_cursor: null. The
single-page form is
client.agentSessions.list({ limit, cursor }). From
0.6.0 (not published yet), both take
status (for example
{ status: 'open' }) to list only the sessions with that
status; iterate keeps it on every page.
Error handling
Every SDK method throws a typed DriftstackError on
non-2xx responses. The error carries kind
(discriminator, narrow with ===),
status, type (the RFC 9457 URI),
detail, and any extension fields from the problem
response. Switch on kind for clean type-narrowing:
import { DriftstackError } from '@driftstack/sdk';
try {
await client.agentSessions.create({ mode: 'ai', proxy_id: process.env.DRIFTSTACK_PROXY_ID! });
} catch (err) {
if (err instanceof DriftstackError && err.kind === 'proxy_required') {
// ProxyRequiredError — no proxy_id, so nothing was created
} else if (err instanceof DriftstackError && err.kind === 'rate_limited') {
// RateLimitError subclass — `err.retryAfterSeconds` is set
} else {
throw err;
}
} Driving each step yourself
client.sessions is the direct session API: your code
drives each step itself with navigate,
interact, wait and capture
instead of giving an AI a task. On hosted Driftstack only agent
sessions load pages, so use client.agentSessions for real
browsing. See the
Sessions reference.
Where to go next
- Curated API map — common routes and runnable patterns, with a link to the complete interactive reference.
- Webhooks — signed events for challenges, profile saves, API keys and crypto orders.
- Error codes — the stable RFC 9457 type URIs the SDK's typed errors carry.
Need help?
[email protected]. Reply times follow your plan's support on the pricing page: a 48-hour reply target on Starter and Pro.