Skip to content

Quickstart

Quickstart

Go from zero to a live, full-duplex conversation with a Casola avatar: get an API key, mint a session from your server, and connect the browser directly to the GPU edge. Under five minutes.

How it fits together (two planes — this shape matters for everything below):

  1. Control plane — your server calls https://api.casola.ai with your secret key to mint a session. The response names the GPU edge to talk to (connect_url) and a short-lived signed token for it (session_token).
  2. Data plane — your client connects straight to that edge over two WebSockets (video down, microphone up). Your secret key never touches the client; media never proxies through your server.

1. Create an account and get an API key

Sign up at the dashboard and open the API Keys page. Click Create key, give it a name, and copy the key — it is shown only once and looks like:

avatar_<8-hex-chars>.<56-hex-chars>

This is a secret key: keep it server-side only (env var, secret manager — never a client bundle or app binary).

Terminal window
export CASOLA_API_KEY="avatar_abc12345.xyz..."

2. Verify the key

Terminal window
curl https://api.casola.ai/api/v1/tokens \
-H "Authorization: Bearer $CASOLA_API_KEY"

A 200 with your token list confirms the key works.

3. Pick a stock avatar

The stock catalog is public — no auth:

Terminal window
curl https://api.casola.ai/api/gallery
{
"avatars": [
{ "id": "mei", "name": "Mei", "tagline": "", "blurb": "", "accent": "#e88",
"voiceLang": "zh", "poster": "/api/gallery/mei/face", "hasIntro": true },
{ "id": "david", "name": "David", "tagline": "", "blurb": "", "accent": "#88e",
"voiceLang": "en", "poster": "/api/gallery/david/face", "hasIntro": true }
]
}

id is the persona key you pass when minting. Each persona also has a public looping intro clip (/api/gallery/{id}/intro?lang=en|zh) you can show in your picker UI while the user decides.

4. Start a session (server-side)

Terminal window
curl -X POST https://api.casola.ai/api/v1/sessions \
-H "Authorization: Bearer $CASOLA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "persona": "mei" }'

201 — a GPU seat is yours:

{
"status": "ready",
"session_id": "019...",
"connect_url": "https://box-1.casola.ai",
"session_token": "eyJhbGciOiJFZERTQ...",
"seat_token": "",
"expires_at": 1719000060
}
  • connect_url — the assigned edge’s https base URL; derive WebSocket URLs by swapping to wss.
  • session_token — a short-lived (≈60 s) EdDSA JWT the edge verifies offline. Connect promptly; if it expires, mint again. Once connected, the live session runs up to the server-set cap (default ≈5 minutes).

202 — fleet at capacity, you’re queued:

{ "status": "queued", "ticket": "q_...", "ahead": 3, "queue_ws": "/api/v1/sessions/queue?ticket=q_..." }

Open the queue_ws WebSocket for live position updates, then re-mint when promoted (see Sessions).

Hand connect_url + session_token to your client — that’s the only thing your backend needs to produce.

5. Connect and talk (browser)

Terminal window
npm install @casola/avatar-client
import { AvatarSession, connectViaToken } from '@casola/avatar-client'
// Your backend endpoint wrapping step 4:
const { connect_url, session_token } = await fetch('/my-backend/start-session').then(r => r.json())
const session = new AvatarSession({
videoEl: document.querySelector('video'),
connect: connectViaToken({ connectUrl: connect_url, sessionToken: session_token }),
workletUrl: '/mic-worklet.js', // see "Worklet asset" below
lang: 'en',
callbacks: {
onFirstFrame() { console.log('avatar is live') },
onPartial(text) { console.log('you (so far):', text) },
onTurn(t) { console.log('you said:', t.text, '→ avatar replies:', t.reply) },
onClose(reason) { console.log('ended:', reason) },
onError(err) { console.error(err) },
},
})
// Must be called from a user gesture (mic permission + autoplay).
await session.start()

That’s the whole conversation loop: the SDK plays the avatar’s video+voice via MSE and streams your microphone up; the avatar listens, thinks, and answers by speakingonPartial/onTurn are transcripts for your UI, not something you need to respond to. Speak over the avatar to interrupt it (barge-in is server-side).

The SDK handles ManagedMediaSource/MediaSource differences (iOS Safari), append queueing, latency housekeeping, mic capture, 48 kHz → 16 kHz resampling, and PCM16 framing. Microphone echoCancellation is always on — required, or the avatar hears itself through your speakers.

Worklet asset. audioWorklet.addModule() needs a real URL — the worklet can’t be inlined into your bundle. With Vite:

import workletUrl from '@casola/avatar-client/worklet?worker&url'
// pass workletUrl to AvatarSession opts

5b. Or connect without the SDK (server / native)

Two WebSockets against connect_url. Browsers must pass the token as ?token=<session_token> (no headers on WS upgrades); native/server clients should prefer the Authorization: Bearer header.

SocketPathDirectionPayload
Downlink/mseserver → client1 text frame {"mime": "..."}, then binary fMP4 segments
Uplink/mic_streamclient → server1 text frame {"op":"hello","lang":"en"}, then binary 16 kHz mono PCM16 frames (1600 samples = 100 ms); JSON partial / turn events come back

See the API Reference → Data plane for the full wire protocol.

Next steps