ask
Send one request and receive a normalized reply.
Use the Python SDK when your client or agent runs in Python.
The package name is thalovant.
Install with pip install thalovant.
pip install thalovantThis flow discovers a public hub, creates a client identity, and sends one request.
from thalovant import ThalovantClient, ThalovantControlPlane
api = ThalovantControlPlane()
public_hubs = api.list_public_hubs(limit=12)for hub in public_hubs["data"]: print(hub["id"], hub["slug"], hub["title"])
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)ThalovantControlPlane() uses https://api.thalovant.com by default.
Accounts with multi-factor authentication enabled must include a TOTP code or a one-time recovery code with the login. Without one, the API rejects the sign-in with HTTP 401 and code mfa_required. Both parameters are sent only when provided. MFA support needs SDK 0.4.21 or newer.
# Or use a one-time recovery code instead:Device login asks your browser to approve the sign-in, so scripts, CLIs, 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 and behaves exactly like after api.login(...).
Device login needs SDK 0.4.22 or newer. Approving the request needs a paid workspace plan; a free plan gets HTTP 402.
api.login_with_browser()
# Optional: narrow the scopes and label the token in the dashboard.api.login_with_browser(scopes=["hubs:read", "clients:write"], client_name="my-cli")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 login_with_browser; the page shows scopes, expiry, and last use, and can revoke the token at any time.
import os
from thalovant import ThalovantControlPlane
api = ThalovantControlPlane(access_token=os.environ["THALOVANT_API_TOKEN"])# Ready immediately; no login call needed.Use this when the identity was downloaded from Thalovant, stored in a secret volume, or saved in your protected SDK config.
from thalovant import ThalovantClient
with ThalovantClient.from_config(profile="prod", protocol="wss") as client: reply = client.ask("What can this hub do?") print(reply.text)from thalovant import ThalovantClient
with ThalovantClient.from_identity_file("_identity.json") as client: reply = client.ask("What can this hub do?") print(reply.text)Environment variables work too:
from thalovant import ThalovantClient
with ThalovantClient.from_env(protocol="https") as client: print(client.ask("Say hello.").text)identity = result.identity
print(identity.enabled_protocols())print(identity.endpoint_for("wss"))print(identity.endpoint_for("https"))print(identity.endpoint_for("mqtt"))
for protocol in ("wss", "https", "mqtt"): if not identity.supports_protocol(protocol): continue if protocol == "mqtt" and identity.mqtt is None: continue
with ThalovantClient(identity, protocol=protocol) as client: print(protocol, client.ask(f"Reply over {protocol}.").text)MQTT requires the identity.mqtt broker credentials returned for that client.
For broker details, see MQTT.
Use a conversation when related turns should share one session.
with ThalovantClient.from_identity_file("_identity.json") as client: 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)from thalovant import ThalovantClient, 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-09-001"},)
with ThalovantClient.from_identity_file("_identity.json") as client: reply = client.ask("Show the next instruction.", context=context) print(reply.text)from thalovant import EVENT_SPEAK, ThalovantClient
with ThalovantClient.from_identity_file("_identity.json", protocol="wss") as client: client.send_utterance("Say the current status.") for event in client.listen(EVENT_SPEAK, timeout=10, max_events=1): print(event.text)ask
Send one request and receive a normalized reply.
listen
Wait for hub events with a timeout.
send_action
Send a button, menu, or tool action.
send_code
Send a QR value, serial number, barcode, or typed code.
For the full method list, see SDK Functions.
Use api.get_operation(operation_id) to follow an accepted command; see Operations.
Since SDK 0.4.25 the control plane can create hubs, runtime groups, and skill installs. Browsing the catalog with api.list_marketplace_skills() needs hubs:read and works on any plan. create_hub, create_runtime_group, install_runtime_group_skill, release_hub, and release_runtime_group need hubs:write and a paid plan.
group = api.create_runtime_group({"name": "kiosks"})hub = api.create_hub({"name": "joke-garden", "runtime_group_id": group["id"], "spec": {}})
hub = api.get_hub(hub["id"])api.update_hub(hub["id"], {"active": False}, etag=hub["etag"])update_hub and delete_hub require the hub’s current etag. See Provision Hubs for the full flow, the immutable fields, and the error table.
| Symptom | Check |
|---|---|
Missing Thalovant API access token |
Call api.login(...) or api.login_with_browser() before private API actions, or pass access_token= to ThalovantControlPlane. |
HTTP 401 with code mfa_required |
Pass otp_code or recovery_code to api.login(...). |
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 mqtt broker credentials. |