Web SDK

@glytos/web puts a conversation with your agent in your page - voice over WebRTC, or text as a chat. Both are in the one package; use whichever you need. It handles the microphone, the connection and the transcript stream.

npm install @glytos/web

The two-step flow

The SDK never sees your API key. Your server mints a short-lived web-call token; the page uses it.

On your server, mint a token naming the agent, and return it to your page:

import { Glytos } from '@glytos/node';

const glytos = new Glytos({ apiKey: process.env.GLYTOS_API_KEY! });
const { token, ws_url } = await glytos.calls.webToken({ workflow_uuid: agentUuid });
// return { token, ws_url } to your page

This is POST /api/v1/calls/web-token with either workflow_uuid (a saved agent) or agent (an inline agent definition) - exactly one of the two. The response carries token, ws_url, workflow_uuid and expires_in, the token's lifetime in seconds. The token is short-lived, so mint it when the visitor starts a call rather than when the page loads. Minting one needs an API key with the workflow:run permission.

In the page, start the call with it:

import { GlytosWebCall } from '@glytos/web';

const call = new GlytosWebCall({ token, wsUrl: ws_url });

call.on('status', status => console.log(status));
call.on('transcript', message => console.log(message.role, message.text));

await call.start();

Mint the token on your server. Putting an API key in the page hands anyone who views source the ability to spend your credit.

Ending a call

call.stop();

Connectivity

The audio travels over WebRTC. By default the SDK gives the browser a single public STUN server (stun:stun.l.google.com:19302) and no TURN server. That is enough on most networks, but a browser behind a symmetric NAT or carrier-grade NAT (common on mobile data and some corporate networks) cannot connect without a TURN relay. Pass your own servers with iceServers:

const call = new GlytosWebCall({
  token,
  wsUrl: ws_url,
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    { urls: 'turn:turn.example.com:3478', username: 'user', credential: 'secret' },
  ],
});

iceServers replaces the default rather than adding to it, so include a STUN server too.

Chat

The same package embeds a text chat. Mint a short-lived chat token on your server (POST /api/v1/chat/token with the agent's workflow_uuid) and hand it to the page:

import { GlytosChat } from '@glytos/web';

const chat = new GlytosChat({ token });

chat.on('token', t => append(t.delta));   // streamed deltas
chat.on('message', m => render(m));       // a complete message
chat.on('error', e => console.error(e));

await chat.start();               // opens the conversation, emits the greeting
await chat.stream('Hello!');      // streamed reply
await chat.send('And this?');     // buffered reply

start, send and stream do not reject when a turn fails: they emit an error event instead, so listen for it. uploadFile is the exception and throws.

Chat requests go to https://api.glytos.com by default. To target another host, pass apiBase as the host root only - for example https://api.glytos.com - because the SDK appends /api/v1/... itself. That differs from the server SDKs, whose baseUrl includes /api/v1.

Attachments, images and resuming

// Attach a file to this conversation only (it does not join the knowledge base)
await chat.uploadFile(fileFromInput, fileFromInput.name);

// Send an image with a turn; a text-only model ignores it
await chat.send('What is on this label?', { images: [dataUri] });

// Persist the conversation id and resume it later
localStorage.setItem('glytos_chat', chat.sessionId ?? '');

const saved = localStorage.getItem('glytos_chat');
const resumed = new GlytosChat({ token, sessionUuid: saved || undefined });
await resumed.send('Where were we?'); // continues the saved conversation

To resume, go straight to send or stream. Do not call start() on a resumed chat: start() always opens a new conversation and replaces the saved id. Chat tokens expire too, so a conversation resumed later needs a freshly minted token for the same agent.

Per-turn instructions are deliberately not available here: this runs in a visitor's browser, so such a field would let anyone rewrite your agent's behaviour on your own site. Use a server SDK for that - see Chat.

Requirements

A voice call needs the page served over HTTPS - browsers only grant microphone access on a secure origin - and the user must grant microphone permission when prompted. Chat has neither requirement.