Skip to content
Console

Go SDK

Use the Go SDK when your client needs a compact service, gateway, command-line tool, or agent process.

The module path is github.com/thalovant/thalovant-go-sdk.

Go SDK v0.3.0 and newer require Go 1.25 or newer.

The SDK is a public Go module and does not require GitHub credentials or a GOPRIVATE override. go get github.com/thalovant/thalovant-go-sdk selects the latest release. For reproducible builds, install the current verified release from the public Go module proxy:

Terminal window
go get github.com/thalovant/[email protected]
package main
import (
"context"
"fmt"
"log"
thalovant "github.com/thalovant/thalovant-go-sdk"
)
func main() {
ctx := context.Background()
control := thalovant.NewDefaultControlPlane("")
_, err := control.Login(ctx, "[email protected]", "password", "")
if err != nil {
log.Fatal(err)
}
result, err := control.CreateClientIdentityForHubID(ctx, "hub-id", thalovant.BootstrapIdentityOptions{
Name: "go-demo-client",
PreferredProtocols: []thalovant.HubProtocol{
thalovant.ProtocolWSS,
thalovant.ProtocolHTTPS,
thalovant.ProtocolMQTT,
},
})
if err != nil {
log.Fatal(err)
}
client, err := thalovant.NewClientWithOptions(result.Identity, thalovant.ClientOptions{
Protocol: thalovant.ProtocolWSS,
})
if err != nil {
log.Fatal(err)
}
defer client.Close(ctx)
reply, err := client.Ask(ctx, "Tell me a short clean joke.", thalovant.RequestOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.Text)
}

NewDefaultControlPlane uses https://api.thalovant.com. Use NewControlPlane only for local development or a self-hosted control plane.

Accounts with multi-factor authentication enabled must include a TOTP code or a one-time recovery code with the login. A plain Login call is rejected with HTTP 401 and code mfa_required. Use LoginWithOptions; the fields are sent only when set. MFA support needs SDK v0.3.2 or newer.

_, err := control.LoginWithOptions(ctx, "[email protected]", "password", thalovant.LoginOptions{
OTPCode: "123456",
})
// Or use a one-time recovery code instead:
_, err = control.LoginWithOptions(ctx, "[email protected]", "password", thalovant.LoginOptions{
RecoveryCode: "abcd-efgh-ijkl",
})

Device login asks your browser to approve the sign-in, so CLIs, services, and agents never handle your password. The SDK prints a short code and the address https://dash.thalovant.com/activate, opens that page when it can, and waits while you approve the request in the dashboard with your normal sign-in, including Google sign-in or MFA. On approval the SDK holds a scoped, revocable API token.

Device login needs SDK v0.3.3 or newer. Approving the request needs a paid workspace plan; a free plan gets HTTP 402.

token, err := control.LoginWithBrowser(ctx, thalovant.DeviceLoginOptions{
Scopes: []string{"hubs:read", "clients:write"}, // optional
ClientName: "my-cli", // optional label in the dashboard
})
if err != nil {
log.Fatal(err)
}
fmt.Println("signed in, token id:", token["token_id"])

Manage the resulting token on the dashboard’s API Tokens page.

Pass a stored API token when the process should start authenticated, such as CI jobs and services. Mint one on the API Tokens page or with LoginWithBrowser; the page shows scopes, expiry, and last use, and can revoke the token at any time.

control := thalovant.NewDefaultControlPlane(os.Getenv("THALOVANT_API_TOKEN"))
page, err := control.ListHubs(ctx, 50, "", "")
client, err := thalovant.NewClientFromConfig("", "prod")
if err != nil {
log.Fatal(err)
}
defer client.Close(ctx)

Raw identity files work too:

client, err := thalovant.NewClientFromFile("_identity.json")
if err != nil {
log.Fatal(err)
}
defer client.Close(ctx)
reply, err := client.Ask(ctx, "What can this hub do?", thalovant.RequestOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Println(reply.Text)

Environment variables work too:

client, err := thalovant.NewClientFromEnv()
if err != nil {
log.Fatal(err)
}
identity := result.Identity
fmt.Println(identity.EnabledProtocols())
fmt.Println(identity.EndpointFor(thalovant.ProtocolWSS))
fmt.Println(identity.EndpointFor(thalovant.ProtocolHTTPS))
fmt.Println(identity.EndpointFor(thalovant.ProtocolMQTT))
for _, protocol := range []thalovant.HubProtocol{
thalovant.ProtocolWSS,
thalovant.ProtocolHTTPS,
thalovant.ProtocolMQTT,
} {
if !identity.SupportsProtocol(protocol) {
continue
}
if protocol == thalovant.ProtocolMQTT && identity.MQTT == nil {
continue
}
client, err := thalovant.NewClientWithOptions(identity, thalovant.ClientOptions{Protocol: protocol})
if err != nil {
log.Fatal(err)
}
reply, err := client.Ask(ctx, fmt.Sprintf("Reply over %s.", protocol), thalovant.RequestOptions{})
_ = client.Close(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(protocol, reply.Text)
}

MQTT requires the Identity.MQTT broker credentials returned for that client.

For broker details, see MQTT.

requestContext := thalovant.BuildClientContext(nil, thalovant.ClientContextOptions{
UserID: "user-42",
UserName: "Ada",
AuthProvider: "oidc",
Source: "checkout-kiosk",
Platform: "kiosk",
Locale: "en-US",
Channel: "chat",
})
reply, err := client.Ask(ctx, "Show the next instruction.", thalovant.RequestOptions{
Context: requestContext,
})
events := client.Transport.Events()
if err := client.SendUtterance(ctx, "Say the current status.", thalovant.RequestOptions{}); err != nil {
log.Fatal(err)
}
select {
case event := <-events:
fmt.Println(event.Name, event.Text())
case <-time.After(12 * time.Second):
log.Fatal("timed out waiting for a hub event")
}

Ask

Send one request and receive normalized text, speech, and display items.

Conversation

Keep related turns in one session.

SendAction

Send a button, menu, or tool action.

SendCode

Send a scanned value, serial number, QR value, or typed code.

For the full method list, see SDK Functions. Use control.GetOperation(ctx, operationID) to follow an accepted command; see Operations.

Since SDK v0.3.6 the control plane can create hubs, runtime groups, and skill installs. Browsing the catalog with ListMarketplaceSkills needs hubs:read and works on any plan. CreateHub, CreateRuntimeGroup, InstallRuntimeGroupSkill, ReleaseHub, and ReleaseRuntimeGroup need hubs:write and a paid plan.

group, err := api.CreateRuntimeGroup(ctx, map[string]any{"name": "kiosks"})
hub, err := api.CreateHub(ctx, map[string]any{
"name": "joke-garden",
"runtime_group_id": group["id"].(string),
"spec": map[string]any{},
}, thalovant.HubCreateOptions{})
hub, err = api.GetHub(ctx, hub["id"].(string))
hub, err = api.UpdateHub(ctx, hub["id"].(string), map[string]any{"active": false}, hub["etag"].(string))

UpdateHub and DeleteHub take the hub’s current etag as a required argument. See Provision Hubs for the full flow, the immutable fields, and the error table.

Symptom Check
Missing access token Call control.Login(...) or control.LoginWithBrowser(...) before private API actions, or pass a token to NewDefaultControlPlane.
HTTP 401 with code mfa_required Use control.LoginWithOptions(...) with an OTPCode or RecoveryCode.
API access requires a paid plan Upgrade the workspace before provisioning private resources through the API.
Unsupported protocol Enable that protocol on the hub and create a fresh identity.
MQTT fails immediately Confirm the identity has Identity.MQTT broker credentials.