Skip to main content
Driftstack DRIFTSTACK

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.

1. Install

go get github.com/driftstackdev/driftstack-api/packages/sdk-go@latest

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() {
    client, err := driftstack.NewClient(driftstack.Config{
        APIKey: os.Getenv("DRIFTSTACK_API_KEY"),
        // BaseURL defaults to https://api.driftstack.dev; override
        // for staging or self-hosted deployments.
    })
    if err != nil {
        panic(err)
    }
    _ = 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{
    Archetype: "default",
    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 "" when there are no more pages; refeed it as
// query.Cursor on subsequent calls 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 7807 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 in-flight SDK calls. No goroutine leaks even under heavy concurrent load.

Where to go next

Need help?

[email protected]. We respond within one business day.