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
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.
Streaming
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
ErrConflict; a
variant that does not match the type returns ErrValidation.
Errors
Every non-2xx is a problem document mapped to*splox.APIError — Status,
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
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.

