Skip to content
Console

SDK Functions

Use this page when you want to build with the SDK, not memorize every method name.

Start with the quick example, then copy the recipe closest to your task. Each recipe shows the same idea in Python, Node.js, Go, and Rust where the SDKs expose it. The Quick Start, Sign In, and Create A Client Identity recipes also show Kotlin, Swift, and C#; for the full surface of those SDKs, use the Kotlin, Swift, and .NET SDK pages.

This example signs in, creates a client identity for one hub, connects, sends one request, and prints the reply.

from thalovant import ThalovantClient, ThalovantControlPlane
api = ThalovantControlPlane()
api.login("[email protected]", "password")
result = api.create_client_identity(
"hub-id",
name="python-demo-client",
preferred_protocols=("wss", "https", "mqtt"),
)
with ThalovantClient(result.identity, protocol="wss") as client:
reply = client.ask("Tell me a short clean joke.")
print(reply.text)

The Kotlin, Swift, and C# clients connect over WSS only in their v0.1 releases, so they take no protocol argument here. See Capability Tiers.

Expected result:

Why did the hub keep good logs? It wanted every punchline to be traceable.

Control plane

Discover hubs and create client identities.

Identity

Load saved credentials and choose a protocol.

Runtime client

Connect, ask, emit events, and send structured input.

Events and context

Listen for hub events and attach session metadata.

Use this before hub discovery or identity provisioning.

from thalovant import ThalovantControlPlane
api = ThalovantControlPlane()

Sign in before private control-plane actions such as creating a client identity.

Accounts with multi-factor authentication enabled must include a TOTP code or a one-time recovery code; without one, the API rejects the sign-in with HTTP 401 and code mfa_required. Every SDK sends the MFA fields only when they are set.

api.login("[email protected]", "password")
# MFA-enabled accounts add a TOTP code or a one-time recovery code:
api.login("[email protected]", "password", otp_code="123456")
api.login("[email protected]", "password", recovery_code="abcd-efgh-ijkl")

Common failure:

Missing Thalovant API access token

Fix it by signing in before the private call, or by passing an API token to the control-plane client. An HTTP 401 with code mfa_required means the account needs the TOTP or recovery code shown above.

Use device login when the code should never see your password: CLIs, agents, notebooks, and machines you do not fully trust with account credentials.

The SDK prints a line like To sign in, visit https://dash.thalovant.com/activate and enter the code XXXX-XXXX, opens that page in your browser when it can, and polls the API while you approve the sign-in with your normal browser session, including Google sign-in or MFA. On approval the SDK receives a scoped, revocable API token and stores it exactly like a password login.

Approving a device sign-in needs a paid workspace plan; on a free plan the approval is rejected with HTTP 402. Device login needs Python 0.4.22, Node.js 0.2.25, Go v0.3.3, Rust v0.2.20, or Kotlin, Swift, and .NET 0.1.1.

api.login_with_browser()
# Optional: request narrower scopes and label the token in the dashboard.
api.login_with_browser(scopes=["hubs:read", "clients:write"], client_name="my-cli")

Every SDK honors the same options: scopes to narrow what the token can do, client_name (native casing) to label the token in the dashboard, open_browser to skip opening the page, prompt to replace the printed line, and a timeout that defaults to 15 minutes.

The approving user manages the resulting token on the dashboard’s API Tokens page: scopes, expiry, last use, and revocation.

Use a stored API token when the process should start authenticated, such as CI jobs, servers, and AI agent configs.

Mint a token on the dashboard’s API Tokens page or through device login, then pass it to the control-plane constructor. Tokens are scoped, revocable, and show their last use on that page; creating one needs a paid workspace plan. The examples read the token from a THALOVANT_API_TOKEN environment variable, the same convention the MCP server uses.

import os
from thalovant import ThalovantControlPlane
api = ThalovantControlPlane(access_token=os.environ["THALOVANT_API_TOKEN"])
# Ready immediately; no login call needed.

Use public hub discovery before you ask a user to choose a hub.

page = api.list_public_hubs(limit=12)
for hub in page["data"]:
print(hub["id"], hub["slug"], hub["title"])

Use this when you have a hub reference from a URL, card, or saved choice.

hub = api.get_public_hub("joke-garden")
print(hub["title"], hub["public_ref"])

Use this for hubs the signed-in workspace can see.

page = api.list_hubs(limit=25)
for hub in page["data"]:
print(hub["id"], hub["title"])

Use this after you already know the hub ID.

hub = api.get_hub("hub-id")
print(hub["title"])

Use this when your code should create the hub. Discovery needs hubs:read and works on any plan. The four writes need hubs:write and a paid plan. For the etag rule, the immutable fields, and the error table, see Provision Hubs.

skills = api.list_marketplace_skills()["data"]
group = api.create_runtime_group({"name": "kiosks"})
hub = api.create_hub({
"name": "joke-garden",
"runtime_group_id": group["id"],
"spec": {"protocols": {"wss": {"enabled": True}}},
})
api.install_runtime_group_skill(group["id"], "skill-weather")
api.release_runtime_group(group["id"], channel="stable")
api.release_hub(hub["id"], channel="stable")

Kotlin, Swift, and .NET expose the same calls with native names. Their SDK pages show the language-specific form.

Hub update and delete need the hub’s current etag, sent as If-Match. Read the hub first: the etag is a body field, and the API sends no ETag response header.

hub = api.get_hub("hub-id")
hub = api.update_hub("hub-id", {"active": False}, etag=hub["etag"])
api.delete_hub("hub-id", etag=hub["etag"])

A missing or stale etag fails with HTTP 412 and changes nothing. Re-read the hub and retry.

Use the operation ID returned by an asynchronous control-plane command. Stop at ready, failed, or timed_out; Git-only commands stop at committed.

operation = api.get_operation("operation-id")
print(operation.status)

See Operations for lifecycle and retry guidance.

Create an identity when a browser, service, voice client, device, or agent needs to connect to one hub.

result = api.create_client_identity(
"hub-id",
name="checkout-kiosk",
preferred_protocols=("wss", "https"),
)
identity = result.identity

Every SDK also accepts an active option on creation, and it defaults to true. Pass false to provision the client disabled, so it cannot connect until you activate it with a client update.

Use config or a downloaded identity file when the client already exists.

On Linux and macOS, protect local config.yaml and _identity.json files with owner-only permissions before loading them:

Terminal window
chmod 600 ~/.config/thalovant/config.yaml
chmod 600 _identity.json
from thalovant import ThalovantClient, ThalovantIdentity
identity = ThalovantIdentity.from_config(profile="prod")
same_identity = ThalovantIdentity.from_file("_identity.json")
client_from_config = ThalovantClient.from_config(profile="prod", protocol="wss")
client_from_file = ThalovantClient.from_identity_file("_identity.json")
client_from_env = ThalovantClient.from_env()

Use protocol helpers before forcing WSS, HTTPS, or MQTT.

print(identity.enabled_protocols())
print(identity.endpoint_for("wss"))
print(identity.endpoint_for("https"))
print(identity.endpoint_for("mqtt"))
print(identity.supports_protocol("wss"))
if identity.mqtt:
print(identity.mqtt.endpoint)

Use a runtime client after you have identity material.

from thalovant import ThalovantClient
client = ThalovantClient(identity, protocol="wss")
client.connect()
print(client.healthcheck())
client.close()

Most code should use a context manager, try/finally, defer, or ? cleanup so connections close reliably.

Use ask for one request-response interaction.

reply = client.ask("What can this hub do?")
print(reply.text)
for item in reply.display_items():
print(item)

Expected output shape:

{
"text": "This hub can answer quick joke and trivia requests.",
"display_items": []
}

Use raw events when you already have an event name and a structured payload.

client.emit(
"thalovant.client.status",
{"state": "ready"},
{"source": "checkout-kiosk"},
)

Use an utterance when the input should behave like speech or chat text from a client.

client.send_utterance(
"What is the status?",
lang="en-us",
session_id="status-session",
)

Use actions for buttons, menu picks, confirmations, and tool commands.

client.send_action(
"/approve invoice-42",
title="Approve invoice",
session_id="approval-session",
)

Use code input for QR values, barcode scans, serial numbers, short codes, or typed exact values.

client.send_code(
"INV-2026-001",
kind="invoice",
session_id="scan-session",
)

Use a conversation when related turns should share the same session.

with client.conversation(lang="en-us") as convo:
print(convo.ask("Remember that my favorite color is blue.").text)
print(convo.ask("What color did I mention?").text)

Use this when one event proves the request completed.

from thalovant import EVENT_UTTERANCE_HANDLED
event = client.wait_for_event(EVENT_UTTERANCE_HANDLED, timeout=12)
print(event.text)

Use event streams for long-running clients, live status, speech output, and UI updates.

from thalovant import EVENT_SPEAK
for event in client.listen(EVENT_SPEAK, timeout=30, max_events=3):
print(event.text)

Context carries stable user, source, locale, and trace metadata without changing the utterance.

from thalovant import build_client_context
context = build_client_context(
user_id="user-42",
user_name="Ada",
source="checkout-kiosk",
platform="kiosk",
locale="en-US",
metadata={"trace_id": "req-2026-06-10-001"},
)
reply = client.ask("Show the next instruction.", context=context)
print(reply.text)

Use diagnostics when a client cannot connect, a protocol is missing, or an event never arrives.

print(client.doctor())

Use the SDK protocol enum or string that matches your language.

Protocol Python Node.js Go Rust
WSS "wss" "wss" thalovant.ProtocolWSS HubProtocol::Wss
HTTPS "https" "https" thalovant.ProtocolHTTPS HubProtocol::Https
MQTT "mqtt" "mqtt" thalovant.ProtocolMQTT HubProtocol::Mqtt

When no protocol is forced, SDKs prefer WSS, then HTTPS, then MQTT when the identity includes broker credentials.

  1. Discover a hub. Use list_public_hubs, get_public_hub, list_hubs, or get_hub.
  2. Create or load identity material. Use create_client_identity for new clients, or load an existing identity from config, file, or environment.
  3. Choose a protocol. Start with WSS unless your deployment needs HTTPS or MQTT.
  4. Send one request. Use ask first because it gives a clear reply.
  5. Add events and context. Add listen, wait_for_event, and build_client_context once the basic request works.

Use this table only when you already know the task and need the language-specific name.

Task Python Node.js Go Rust
Create API client ThalovantControlPlane() new ThalovantControlPlane() NewDefaultControlPlane(token) ControlPlane::default()
Sign in login(email, password) login(email, password) Login(ctx, email, password, scope) login(email, password, scope)
Sign in with MFA login(..., otp_code=..., recovery_code=...) login(..., { otpCode, recoveryCode }) LoginWithOptions(ctx, email, password, options) login_with_options(email, password, options)
Sign in without a password login_with_browser(...) loginWithBrowser(options) LoginWithBrowser(ctx, options) login_with_browser(options)
Use an API token ThalovantControlPlane(access_token=...) new ThalovantControlPlane(url, { accessToken }) NewDefaultControlPlane(token) ControlPlane::with_access_token(token)
List public hubs list_public_hubs(...) listPublicHubs(...) ListPublicHubs(ctx, limit, cursor) list_public_hubs(limit, cursor)
Get public hub get_public_hub(ref) getPublicHub(ref) GetPublicHub(ctx, ref) get_public_hub(ref)
List visible hubs list_hubs(...) listHubs(...) ListHubs(ctx, limit, cursor, ownerID) list_hubs(limit, cursor, owner_id)
Get hub get_hub(hub_id) getHub(hubId) GetHub(ctx, hubID) get_hub(hub_id)
Get operation get_operation(operation_id) getOperation(operationId) GetOperation(ctx, operationID) get_operation(operation_id)
Create identity create_client_identity(...) createClientIdentity(...) CreateClientIdentityForHubID(...) create_client_identity_for_hub_id(...)

The provisioning calls have their own naming table on Provision Hubs.

Task Python Node.js Go Rust
Load identity from config ThalovantIdentity.from_config(...) ThalovantIdentity.fromConfig(...) IdentityFromConfig(path, profile) Identity::from_config(profile)
Load identity file ThalovantIdentity.from_file(path) ThalovantIdentity.fromFile(path) IdentityFromFile(path) Identity::from_file(path)
Load client from file ThalovantClient.from_identity_file(path) ThalovantClient.fromIdentityFile(path) NewClientFromFile(path) Client::from_file(path)
Load client from config ThalovantClient.from_config(...) ThalovantClient.fromConfig(...) NewClientFromConfig(path, profile) Client::from_config(profile)
Load client from env ThalovantClient.from_env() ThalovantClient.fromEnv() NewClientFromEnv() Client::from_env()
Enabled protocols enabled_protocols() enabledProtocols() EnabledProtocols() enabled_protocols()
Endpoint for protocol endpoint_for(protocol) endpointFor(protocol) EndpointFor(protocol) endpoint_for(protocol)
Check protocol supports_protocol(protocol) supportsProtocol(protocol) SupportsProtocol(protocol) supports_protocol(protocol)
MQTT credentials identity.mqtt identity.mqtt Identity.MQTT identity.mqtt
Task Python Node.js Go Rust
Create protocol client ThalovantClient(identity, protocol="wss") new ThalovantClient(identity, { protocol }) NewClientWithOptions(identity, ClientOptions{Protocol: ...}) Client::with_protocol(identity, protocol)
Connect connect() connect() Connect(ctx) connect()
Close close() close() Close(ctx) close()
Health healthcheck() healthcheck() Healthcheck() healthcheck()
Ask ask(text, ...) ask(text, options) Ask(ctx, text, options) ask(text, options)
Emit raw event emit(event, data, context) emit(event, data, context) Emit(ctx, event, data, context) emit(event, data, context)
Send utterance send_utterance(text, ...) sendUtterance(text, options) SendUtterance(ctx, text, options) send_utterance(text, options)
Send action send_action(payload, ...) sendAction(payload, options) SendAction(ctx, payload, options) send_action(payload, options)
Send code send_code(value, ...) sendCode(value, options) SendCode(ctx, value, options) send_code(value, options)
Conversation conversation(...) conversation(options) Conversation(options) conversation(options)
Task Python Node.js Go Rust
Wait for event wait_for_event(name, ...) waitForEvent(name, options) client.Transport.Events() with timeout client.transport.subscribe() with timeout
Stream events listen(name, ...) on(name, handler, options) client.Transport.Events() client.transport.subscribe()
Diagnostics doctor() healthcheck() Healthcheck() healthcheck()
Build context build_client_context(...) buildClientContext(...) BuildClientContext(...) build_client_context(...)
Response text reply.text reply.text reply.Text reply.text
Display items reply.display_items(...) reply.displayItems(...) reply.DisplayItems(...) reply.display_items(...)
Event text event.text event.text event.Text() event.text()