@voicethere/client
@voicethere/client connects web and Node runtimes to VoiceThere sessions over WebRTC. Current npm cut: 0.8.3. Requires Node.js 22+ for the Node entry. Source: voicethere/client. Wire protocol: @node-webrtc-rust/signaling.
This page is the package map. Browser connect, iframe permissions, and autoplay live in Browser client. Widget designer and CDN JSON live in Embed widget.
Install
npm install @voicethere/client
Use a client API key (vthc_…) in browsers — create one in the dashboard or voicethere api-keys create --kind client. Do not embed org/project vth_ keys.
Entry points
| Import | For |
|---|---|
@voicethere/client | connectVoiceSession — local or cloud, shared by Node scripts and simple browsers. |
@voicethere/client/browser | startSession, connectBrowserSession, mic UI helpers, captions. |
@voicethere/client/embed | Floating widget launcher. CDN script is a built copy of this entry. |
@voicethere/client/node | Headless createNodeWebRtcRuntime for automated tests (needs optional peer @node-webrtc-rust/sdk + ws). |
Local vs cloud
| Mode | When | Signaling |
|---|---|---|
local | Agent runner on your machine | ws://127.0.0.1:8080/ws |
cloud | Hosted VoiceThere session | wss://signaling…/ws?token=join token from the sessions API |
import { connectVoiceSession } from "@voicethere/client";
const client = await connectVoiceSession({
mode: "local",
signalingUrl: "ws://127.0.0.1:8080/ws",
sessionId: "local-dev",
});
client.on("peer-joined", (peerId) => console.log("peer", peerId));Browser session
import {
startSession,
connectBrowserSession,
} from "@voicethere/client/browser";
const provision = await startSession({
apiBase: "https://sessions.voicethere.io/v1",
projectId,
headers: { Authorization: `Bearer ${apiKey}` },
onSessionError: (event) => console.error(event.code, event.message),
});
if (provision.ok) {
await connectBrowserSession({
mode: "voice",
credentials: provision.credentials,
customerContext: { userId: "u_123" },
onSessionError: (event) => console.error(event.code, event.message),
});
}Production sessions host: https://sessions.voicethere.io/v1. Each browser tab must call startSession() once — one orchestrator session id per client. Do not share join credentials across tabs. Allowed origins: Browser CORS.
peerId
When talking to a VoiceThere runner (or any server using VoiceAgentSessionHost):
- Omit
peerId— the SDK generatesclient-<random>(recommended). - Or pass an id that starts with
client-. - Bare labels such as
user-1join signaling but never receive a WebRTC offer.
Reuse the same peerId for reconnectPolicy: "same-session".
Reconnect and billing
| Action | What happens |
|---|---|
| Dashboard Reconnect or widget Connect after hangup | startSession() → new orchestrator session, new billing period once WebRTC connects |
| Unintentional drop | Default reconnectPolicy: "same-session" re-joins with the same credentials and peerId |
session.reconnect() | Same session — re-opens signaling only |
session.disconnect() / disconnectAsync() | client_disconnected |
session.sendCloseSignal() | client_close_signal |
Billing starts when the runner reports a billable WebRTC leg (voice: connected peer connection + open control channel + agent; data-only: PC + data channel). Provision alone does not bill. Pass reconnectPolicy: "new-session" to disable automatic same-session retry. Idle timeout ends a quiet session with idle_timeout.
Session errors
Pass onSessionError to startSession and connectBrowserSession for provisioning failures, WebRTC errors, and runner session_error events. Legacy { type: "agent_error" } payloads map to AGENT_CHILD_CRASHED. Catalog: Session errors.
Register setRootConnectionErrorHandler once for process-wide signaling/WebRTC logging without attaching .on("error") on every connection.
Embed widget
Import from @voicethere/client/embed. CDN JSON (VoiceThereWidgetConfigV1) and inline options support preset, position, theme (including chat bubble fonts and colors), and customCss. widget.updateConfig({ … }) restyles without remounting. Client 0.8.2+ loads published fields from the CDN URL in the Access embed snippet. streamSpokenText typewrites agent captions when the agent calls speakAndChat({ stream: true }).
CSS hooks: .vt-widget, .vt-widget-launcher, .vt-widget-panel, message --incoming / --outgoing bubbles. Variables include --vt-color-primary, --vt-font-ui, --vt-panel-width. Export constants: WIDGET_CSS_CLASSES, WIDGET_CSS_VARIABLES.