Browser SDK
@zentiq/client joins a live session in the browser: it shows the avatar in a <video> element, plays its voice, uses the microphone, and tells you what is said and who is speaking. It never needs your API key: your server starts the session and hands the browser the answer of POST /v1/sessions/tokens.
npm install @zentiq/client@^1Coming to npm
@zentiq/client 1.0.0 is being released with the 2026-10-01 contract. npm has 0.2.0, an earlier version. The command above installs 1.0.0 or a later 1.x only, so it works as soon as 1.0.0 is out.npm 7 or later also installs the one package it needs alongside it; with another package manager, install the peer dependency its package.json names.
Join#
import { joinSession, type SessionToken } from "@zentiq/client";
const token = (await fetch("/zentiq/session", { method: "POST" }).then((r) => r.json())) as SessionToken;
// People must know they are talking to an AI: show the persona's words for the whole session.
const disclosure = document.querySelector<HTMLElement>("#disclosure");
if (disclosure && token.disclosure?.required) disclosure.textContent = token.disclosure.text;
const session = await joinSession(token, {
video: document.querySelector<HTMLVideoElement>("#avatar")!,
onTranscript: (t) => {
if (t.final) console.log(t.role === "user" ? "You" : t.speaker ?? token.persona.name, t.text);
},
onSpeaking: (who) => document.body.setAttribute("data-speaking", who),
});joinSession(token, options) takes the whole token answer (it names the persona, so a duo's two people are known). The options:
| Option | What it does |
|---|---|
video | The <video> element the avatar appears in (autoplay, playsinline). |
audio | Another element for the voice, if you want one. |
microphone | Start with the microphone on (default true). |
onTranscript(t) | Captions: { role, text, final, speaker }, and for a duo's turn, lines with each person's words. |
onSpeaking(who) | "avatar", "user" or "none". |
onSpeakerChange(name) | A duo: the person speaking now, or null. |
onEvent(e) | The session's live events, each with seq, type, ts and data: what the person said (learner.transcript.partial, learner.transcript.final), the avatar's whole turn (tutor.transcript.final) and, for a line your server sent with sessionSay, avatar.speaking.start and avatar.speaking.end. New types can appear: ignore the ones you don't handle. |
onReady, onEnd, onError | When the session is joined, when it ends, when something fails. |
It answers with a session: setMicrophoneEnabled(on), end(), the cast (who is on the stage) and sessionId.
Leaving and ending#
document.querySelector("#end")?.addEventListener("click", async () => {
session.end(); // leaves the room
// …and your server ends the session, so its minutes stop now.
await fetch("/zentiq/end", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId: token.sessionId }) });
});session.end() leaves the room. It doesn't end the session: your server does, with POST /v1/sessions/{id}/end, and its minutes stop then. If the page just closes, Zentiq's clean-up ends the session a few minutes after the room is found empty.
Duos#
A duo's token names both people ("Mark & Susan"). The SDK then knows who is on the stage (session.cast.members, left then right), names the speaker on every caption, and calls onSpeakerChange as the floor passes from one to the other. castOf(name) and namedLines(text, cast) are exported for your own interface.
The AI label#
joinSession draws an AI label on the avatar's video for the whole session, in the session's language (the token's disclosure.label), and shows the token's notice for a few seconds when the session starts. It marks the video data-ai-generated="true" and describes it with the label for screen readers. It needs no style sheet, and the video's container becomes its frame (a container that is position: static is set to relative).
aiLabel option | What it does |
|---|---|
position | "top-left" (default), "top-right", "bottom-left" or "bottom-right". |
className, style | Restyle it. A style can't hide it. |
text | Your own words; they must still say it's an AI. |
noticeMs | How long the start notice stays (at least 3 seconds). |
show: false | Hide it, only when the token's disclosure.ownLabel is true. |
In full screen, put the video's container in full screen, not the video. See Telling people it's an AI.