Skip to content
Console

Node SDK

Use the Node SDK when your client or agent runs in JavaScript or TypeScript.

The package name is @thalovant/sdk. Node.js 20 or newer is required.

Install with npm install @thalovant/sdk.

Terminal window
npm install @thalovant/sdk
import { ThalovantClient, ThalovantControlPlane } from "@thalovant/sdk";
const api = new ThalovantControlPlane();
const publicHubs = await api.listPublicHubs({ limit: 12 });
for (const hub of publicHubs.data as Array<{ id: string; slug: string; title: string }>) {
console.log(hub.id, hub.slug, hub.title);
}
await api.login("[email protected]", "password");
const result = await api.createClientIdentity("hub-id", {
name: "node-demo-client",
preferredProtocols: ["wss", "https", "mqtt"],
});
const client = new ThalovantClient(result.identity, { protocol: "wss" });
try {
const reply = await client.ask("Tell me a short clean joke.");
console.log(reply.text);
} finally {
await client.close();
}

new 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 options are sent only when provided. MFA support needs SDK 0.2.23 or newer.

await api.login("[email protected]", "password", { otpCode: "123456" });
// Or use a one-time recovery code instead:
await api.login("[email protected]", "password", { recoveryCode: "abcd-efgh-ijkl" });

Device login asks your browser to approve the sign-in, so scripts, bots, 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 0.2.25 or newer. Approving the request needs a paid workspace plan; a free plan gets HTTP 402.

// Prints: To sign in, visit https://dash.thalovant.com/activate and enter the code XXXX-XXXX
const token = await api.loginWithBrowser({ clientName: "my-laptop" });
// api.accessToken is now set, exactly like after api.login(...).
const page = await api.listHubs({ limit: 50 });

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.

const api = new ThalovantControlPlane("https://api.thalovant.com", {
accessToken: process.env.THALOVANT_API_TOKEN,
});
// Ready immediately; no login call needed.
const page = await api.listHubs({ limit: 50 });

The SDK also runs in browsers behind a bundler. This needs SDK 0.2.24 or newer.

The control plane, including login, listPublicHubs, createClientIdentity, memory, and analytics, uses the global fetch. ThalovantClient works over the wss and https protocols using the global WebSocket and Web Crypto for payload encryption.

The package ships a browser map alongside its exports entry, so bundlers such as esbuild, webpack, Vite, and Rollup pick browser-safe modules automatically and never pull ws, mqtt, or node: builtins into web bundles. Bundle it like any other dependency:

Terminal window
esbuild app.js --bundle --platform=browser --outfile=dist/app.js
// app.js — runs in the browser after bundling
import { ThalovantClient, ThalovantControlPlane } from "@thalovant/sdk";
const api = new ThalovantControlPlane();
await api.login(email, password);
const result = await api.createClientIdentity(hubId, { name: "web-kiosk" });
const client = new ThalovantClient(result.identity, { protocol: "wss" });
const reply = await client.ask("Hello from the browser.");
console.log(reply.text);
await client.close();

Browser caveats:

  • The mqtt protocol stays Node-only. Constructing the MQTT transport in a browser throws ThalovantUnsupportedProtocolError with a clear message; use wss or https instead.
  • Identity files and YAML configs stay Node-only. ThalovantIdentity.fromFile(), fromConfig(), and defaultConfigPath() throw in browsers; construct ThalovantIdentity from an in-memory object, such as the result of createClientIdentity.
  • The synchronous crypto helpers throw in browsers; use the *Async variants, which the transports already use on both platforms.
  • A client identity is a secret. Only embed identities scoped to public or kiosk-style hubs in web apps, and request an approved SDK Origin before calling the API from a customer-owned browser domain.
import { ThalovantClient } from "@thalovant/sdk";
const client = await ThalovantClient.fromConfig({ profile: "prod", protocol: "wss" });
try {
const reply = await client.ask("What can this hub do?");
console.log(reply.text);
} finally {
await client.close();
}

Raw identity files work too:

import { ThalovantClient } from "@thalovant/sdk";
const client = await ThalovantClient.fromIdentityFile("_identity.json");
try {
const reply = await client.ask("What can this hub do?");
console.log(reply.text);
} finally {
await client.close();
}

Environment variables work too:

import { ThalovantClient } from "@thalovant/sdk";
const client = ThalovantClient.fromEnv();
const identity = result.identity;
console.log(identity.enabledProtocols());
console.log(identity.endpointFor("wss"));
console.log(identity.endpointFor("https"));
console.log(identity.endpointFor("mqtt"));
for (const protocol of ["wss", "https", "mqtt"] as const) {
if (!identity.supportsProtocol(protocol)) continue;
if (protocol === "mqtt" && !identity.mqtt) continue;
const client = new ThalovantClient(identity, { protocol });
try {
const reply = await client.ask(`Reply over ${protocol}.`);
console.log(protocol, reply.text);
} finally {
await client.close();
}
}

MQTT requires the identity.mqtt broker credentials returned for that client.

For broker details, see MQTT.

import { buildClientContext } from "@thalovant/sdk";
const context = buildClientContext({}, {
userId: "user-42",
userName: "Ada",
source: "checkout-kiosk",
platform: "kiosk",
locale: "en-US",
channel: "chat",
});
const reply = await client.ask("Show the next instruction.", { context });
console.log(reply.text);
import { EVENT_SPEAK, EVENT_UTTERANCE_HANDLED } from "@thalovant/sdk";
const sub = client.on(EVENT_SPEAK, event => {
console.log(event.text);
});
try {
await client.sendUtterance("Say the current status.");
await client.waitForEvent(EVENT_UTTERANCE_HANDLED, { timeoutMs: 12_000 });
} finally {
sub.close();
}

ask

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

waitForEvent

Wait for one matching hub event with a timeout.

sendAction

Send a button, menu, or tool action.

sendCode

Send a barcode, QR value, serial number, or typed exact value.

For the full method list, see SDK Functions. Use api.getOperation(operationId) to follow an accepted command; see Operations.

Since SDK 0.2.28 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.

const group = await api.createRuntimeGroup({ name: "kiosks" });
let hub = await api.createHub({ name: "joke-garden", runtimeGroupId: group.id as string, spec: {} });
hub = await api.getHub(hub.id as string);
await api.updateHub(hub.id as string, { active: false }, { etag: hub.etag as string });

updateHub and deleteHub require the hub’s current etag. See Provision Hubs for the full flow, the immutable fields, and the error table.

Symptom Check
Missing access token Call api.login(...) or api.loginWithBrowser(...) before private API actions, or pass accessToken to ThalovantControlPlane.
HTTP 401 with code mfa_required Pass otpCode or recoveryCode in the login options.
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, and that the client is not running in a browser bundle.