Skip to content

Sessions

A session is one live, full-duplex conversation seat. Mint it here, then connect your client directly to the returned edge — see the data plane for the WebSocket protocol.

Try it →

Create a session

POST /api/v1/sessions

Required scope: sessions:write (secret key) or session:connect (publishable key — stock personas only, origin-checked).

Request body — pick one of persona / avatar_ref:

{ "persona": "mei" }
{ "avatar_ref": "019...", "voice_ref": "019..." }
FieldRequiredDescription
personaOne-ofStock persona key from the public gallery (mei, david, …). Works without a workspace.
avatar_refOne-ofCustom avatar version id (see Avatars). Secret, workspace-bound keys only.
voice_refNoVoice version id override. Secret keys only.
workspace_idConditionalOnly when the key is not workspace-bound (and only for avatar_ref/voice_ref mints).
domain_allowlistNoOrigins permitted to connect with the minted token. Publishable keys: overridden by the key’s registered origins.

Response 201 — seat granted:

{
"status": "ready",
"session_id": "019...",
"connect_url": "https://box-1.casola.ai",
"session_token": "eyJhbGciOiJFZERTQ...",
"seat_token": "",
"expires_at": 1719000060
}
FieldDescription
connect_urlThe assigned edge’s https base URL. Derive WebSocket URLs by swapping httpswss and appending /mse / /mic_stream.
session_tokenShort-lived (≈60 s) EdDSA JWT the edge verifies offline. Browsers pass it as ?token=; servers/native as Authorization: Bearer.
seat_tokenOpaque seat handle (internal; the platform uses it for heartbeat/release plumbing).
expires_atUnix seconds when session_token expires. Connect before this; the live session then runs up to the server-set cap (default ≈300 s).

Response 202 — fleet at capacity, you’re queued:

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

Open queue_ws as a WebSocket for live position events; when promoted, POST /api/v1/sessions again to claim your seat.

Queue WebSocket

GET /api/v1/sessions/queue?ticket={ticket} (WebSocket upgrade)

No API key needed — the ticket is the credential.

Get a session

GET /api/v1/sessions/{sessionId}

Required scope: sessions:read

Returns the session record including its current status (pending, active, or ended).

{
"id": "019...",
"workspace_id": "019...",
"api_token_id": "019...",
"avatar_ref": "019...",
"voice_ref": null,
"status": "pending",
"box_host": "box-1.casola.ai",
"created_at": 1719000000
}

Heartbeat & release

POST /api/v1/sessions/{sessionId}/heartbeat scope: sessions:write
POST /api/v1/sessions/{sessionId}/release scope: sessions:write

Live media traffic is itself the liveness signal — heartbeat only matters for a seat you are holding while not yet connected. release frees the seat early on a clean client exit (promotes the queue head immediately instead of waiting for the idle reaper).