Skip to main content
The Go SDK covers the v2 surface — runs, events, messages, outputs, tree, usage, interactions, harnesses and versions, plus the discovery endpoints — and the v1 MCP tool-execution helper. Go 1.22 or newer, zero external dependencies.
NewClient takes options rather than a struct: splox.WithAPIKey(k), splox.WithBaseURL(u), splox.WithHTTPClient(h), splox.WithMaxRetries(n). The base URL is the server API root — https://splox.io/api by default — and every path is appended already versioned, with no rewriting.

The first call

Services: client.Runs, client.Interactions, client.Harnesses, client.HarnessVersions, client.LLMEndpoints, client.ToolServers, client.MCP.

Reading a run

Message.Text() concatenates the text parts, which is why the tool call and its result print empty — they are json parts in Content. RunUsage carries InputTokens, OutputTokens, ToolCalls, DurationMS, Amount, Currency, Final and UpdatedAt; Amount is a decimal string. Run.ChatID is a *string, nil for a run with no chat. Reads: Runs.Get, Runs.Messages / MessagesPage, Runs.Outputs / OutputsPage, Runs.Tree, Runs.Usage, Runs.List / ListPage.

Creating a run

Wait polls to a terminal status and returns ErrWaitTimeout, with the last observed run, if the deadline passes first. An Idempotency-Key is generated (uuid4) when you do not pin one with CreateRunParams.IdempotencyKey, and a retry always resends the same key.
Runs.Create in v2.0.0 does not send machine_id, which the API now requires:
Until the SDK ships the field, create runs over HTTP — see Runs — and use the SDK for everything after that.

Streaming

The stream reconnects with Last-Event-ID, deduplicates by event id, and closes the channel after the terminal run.status_changed. Anything written to the journal in the instant after that event is only visible through the paged mode, so do one final read if you need the complete journal:
EventsOpts{Stream: false} drains the durable journal and closes when caught up. Sequence is per-run monotonic from 1, and EventsOpts.Cursor resumes.

Iterators

Lists are lazy iterators; ListPage gives you one page and the cursor.

Interactions

Responding to something that is no longer pending returns ErrConflict; a variant that does not match the type returns ErrValidation.

Errors

Every non-2xx is a problem document mapped to *splox.APIErrorStatus, Code, Detail, Errors []InvalidParam, TraceID, RetryAfter, Raw — and matched with sentinels:
ErrBadRequest, ErrUnauthorized, ErrForbidden, ErrNotFound, ErrNotAcceptable, ErrConflict, ErrCursorExpired, ErrValidation, ErrRateLimited, ErrServer. GETs retry up to three times with exponential backoff, honoring Retry-After, on network errors, 429 and 5xx. POSTs retry only when they carry an Idempotency-Key, always the same one. Cancel carries no key and never retries — it is idempotent on the server anyway.

Raw content

ContentPart keeps every part lossless: Raw holds the exact JSON received and Value holds the payload of json parts, so tool_call, tool_result and reasoning bodies — and part types that do not exist yet — survive a round trip unchanged. Output.Value, RunEvent.Data, Interaction.Payload and InteractionResponse.Raw are raw JSON for the same reason.

Ids

EncodeIDBytes / DecodeIDBytes do the same for [16]byte. Every method accepts either form for harness, chat, run and interaction ids.

Harnesses

A Harness is {ID, Name, Versions} and each version ref is {Number, CommitSHA}. Harnesses.ListPage returns index entries without their versions; Harnesses.Get fills them in.

Testing against a real server

The SDK ships a live suite that talks to an actual instance: