Go SDK quickstart
The github.com/driftstackdev/driftstack-api/packages/sdk-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 the streaming surface is a channel.
SDK automation requires an API-enabled paid tier, including any Manual
tier, and a ds_live_… customer API key. Free starts in the
desktop app, whose browser sign-in stores a restricted
ds_test_… device credential automatically; it is not an SDK
or sandbox key.
1. Install
go get github.com/driftstackdev/driftstack-api/packages/sdk-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 (
"context"
"os"
"github.com/driftstackdev/driftstack-api/packages/sdk-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. Start a session
session, err := client.Sessions.Create(ctx, &driftstack.CreateSessionRequest{
// Omit Archetype to inherit the locked launch default
// (iphone17_ios18_7_safari26_4); pass a real registry slug
// to pin a specific device profile.
Purpose: driftstack.PurposeProductionCustomer,
})
if err != nil {
return err
}
log.Println(session.ID, session.Status, session.Archetype) session is a strongly typed struct. The status
string is the SessionStatus type with the constants
SessionCreating, SessionReady,
SessionBusy, SessionDestroyed, and
SessionErrored — switch on those for exhaustive
compile-time coverage. The create call doesn't take a target
URL; drive the session to a URL with
client.Sessions.Navigate(...).
4. Drive and finish the session
There is no built-in wait-until-terminal helper. Drive
the session through its lifecycle with
Navigate, Interact,
Wait, Capture, then
Destroy when you're done. Wait blocks
on a server-side condition (selector, URL, time):
_, err = client.Sessions.Navigate(ctx, session.ID, &driftstack.NavigateRequest{
URL: "https://example.com",
})
if err != nil { return err }
_, err = client.Sessions.Wait(ctx, session.ID, &driftstack.WaitRequest{
Condition: driftstack.NewTimeCondition(1000),
})
if err != nil { return err }
shot, err := client.Sessions.Capture(ctx, session.ID, &driftstack.CaptureRequest{
Kind: "screenshot",
})
if err != nil { return err }
log.Println("captured", shot.ByteSize, "bytes")
// Idempotent.
if err := client.Sessions.Destroy(ctx, session.ID); err != nil {
return err
}
Every method takes a context.Context — parent
cancellations / deadlines propagate cleanly through the HTTP
layer. For batch workloads, prefer
webhooks over polling.
5. Paginate over historical sessions
page, err := client.Sessions.List(ctx, &driftstack.ListSessionsQuery{Limit: 50})
if err != nil { return err }
for _, s := range page.Data {
if s.Status == driftstack.SessionDestroyed {
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 to walk
// the full history. 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 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.
Where to go next
- Curated API map — common routes and runnable patterns, with a link to the complete interactive reference.
- Crypto orders — customer-facing crypto-checkout surface.
- TypeScript SDK — same surface, TS-native.
- Python SDK — same surface, sync + asyncio.
- Webhooks — avoid polling in production.
- Cost monitoring — set alerts before the bill surprises you.
- Error codes — the stable RFC 9457 type URIs the SDK's typed errors carry.
Need help?
[email protected]. We respond within one business day.