Ask
Send one request and receive normalized text, speech, and display items.
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:
package main
import ( "context" "fmt" "log"
thalovant "github.com/thalovant/thalovant-go-sdk")
func main() { ctx := context.Background() control := thalovant.NewDefaultControlPlane("")
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.
OTPCode: "123456",})
// Or use a one-time recovery code instead: 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. |