Go SDK quickstart
The github.com/driftstackdev/driftstack-go module wraps
the same REST surface as the curl
quickstart. Idiomatic Go: every call takes a
context.Context, returns a typed struct + error,
and streaming progress arrives through callbacks
(MessageOptions.OnStep / OnEvent).
SDK automation needs a ds_live_… customer API key. Every
plan includes the API, the free trial too.
1. Install
go get github.com/driftstackdev/driftstack-go@latest
The Go SDK is published as a tagged pre-1.0 module. Commit the resulting
go.mod and go.sum for reproducible deployments.
Go ≥ 1.22 is supported. The module has zero non-stdlib runtime dependencies
(it speaks raw JSON over net/http); no transitive bloat.
2. Construct the client
package main
import (
"os"
"github.com/driftstackdev/driftstack-go"
)
func main() {
// BaseURL defaults to https://api.driftstack.dev; pass
// driftstack.WithBaseURL(...) to override for staging or
// self-hosted deployments. New does not return an error.
client := driftstack.New(os.Getenv("DRIFTSTACK_API_KEY"))
_ = client
}
The constructor does not make any network calls. Reuse one
client across your process — it is internally pooled via
http.Client with sensible defaults (no
reconfiguration needed for production traffic).
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
(ProxyID). You will need:
- A paid plan: Starter, Pro, Team, Scale or Enterprise.
-
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).
agent, err := client.AgentSessions.Create(ctx, &driftstack.CreateAgentSessionRequest{
Mode: "ai",
ProxyID: os.Getenv("DRIFTSTACK_PROXY_ID"),
}, nil)
if err != nil {
return err
}
// Always close it: an open session counts against your plan's
// concurrent-session limit.
defer func() {
if err := client.AgentSessions.Close(context.Background(), agent.ID); err != nil {
log.Printf("close failed: %v", err)
}
}()
// 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 { return err }
if s.Status == "closed" {
reason := "unknown"
if s.ClosedReason != nil { reason = *s.ClosedReason }
return errors.New("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: "task-" + time.Now().Format(time.RFC3339Nano)})
if err != nil { return err }
log.Println(reply.Kind, reply.Answer) 4. What happened
Create started an agent session through the proxy you
named; without a ProxyID it is refused with
422 proxy-required, and nothing is created. A new session
reads Status active at once; the loop waits for
IsReady(), which comes when its iPhone Safari browser has
started.
Message gave the agent the task: Kind is
plan-executed when it ran, with the Answer it
read back, and the idempotency key makes a retried message the same
turn rather than a second one. Close, run by
defer, ends the session and releases its slot.
Every method takes a context.Context — parent
cancellations / deadlines propagate cleanly through the HTTP
layer. 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
page, err := client.AgentSessions.List(ctx, &driftstack.ListAgentSessionsQuery{Limit: 50})
if err != nil { return err }
for _, s := range page.Data {
if s.Status == "closed" {
log.Println(s.ID)
}
}
// page.NextCursor is nil when there are no more pages; otherwise
// pass *page.NextCursor as query.Cursor on the next call, or use
// client.AgentSessions.Iterate to walk the full history.
// From v0.8.0 (not published yet),
// set query.Status (for example "open") to list only the sessions
// with that status; Iterate keeps it on every page. Error handling
Every SDK method returns a typed error wrapper on non-2xx
responses — one per problem class
(ValidationError, RateLimitError,
ConcurrencyLimitError,
NotFoundError, etc.). Use errors.As
to recover the typed shape (lifted extension fields,
RetryAfterSeconds, etc.); use
errors.Is against the sentinel
(ErrRateLimit, ErrValidation, …) when
you just need category matching:
var pr *driftstack.ProxyRequiredError
if errors.As(err, &pr) {
// No ProxyID, so nothing was created.
}
var rl *driftstack.RateLimitError
if errors.As(err, &rl) {
time.Sleep(time.Duration(rl.RetryAfterSeconds) * time.Second)
// retry
}
var ve *driftstack.ValidationError
if errors.As(err, &ve) {
// ve.Problem carries the per-field issues
}
if errors.Is(err, driftstack.ErrConflict) {
// any 409, regardless of subclass
}
Every typed error embeds an apiError shape with
Status, ProblemType (the RFC 9457
URI), Message, and Problem (the full
parsed problem map). Read additional extension fields off
err.Problem.
Context propagation
Every method takes a context.Context as its first
argument. Cancellation cascades through the underlying HTTP
requests, so a parent deadline (or an HTTP request cancellation
in a Gin / chi / standard http.Server handler)
cleanly aborts the underlying in-flight HTTP call.
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.
- TypeScript SDK — same surface, TS-native.
- Python SDK — same surface, sync + asyncio.
- 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.