Sessions
A session is the unit of everything: one running avatar, from creation to teardown. This page covers the full lifecycle beyond the happy path in the Quickstart.
Lifecycle
Section titled “Lifecycle”- Create:
POST /v1/sessionswithavatar_id,mode, and optionallyvoice_id. Returnssession_id,client_token, andwebsocket_url. - Connect: the browser opens
websocket_urland sends{"type": "session.ready"}. See Streaming into your app. - Drive: your server calls
POST /v1/sessions/{id}/speak(or/audio) whenever the avatar should talk, andPOST /v1/sessions/{id}/interruptto cut it off. - Idle (optional):
POST /v1/sessions/{id}/waiting_stateparks the session in its idle loop without ending it, and resumes it later. - Refresh (as needed):
POST /v1/sessions/{id}/tokenmints a newclient_token/websocket_url, for reconnects or before the current token expires. - End:
DELETE /v1/sessions/{id}when the conversation is over, releasing the session’s resources.
Creating a session
Section titled “Creating a session”curl -X POST https://api.2wai.ai/v1/sessions \ -H "Authorization: Bearer $TWAI_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "avatar_id": "avt_9f2c1a4b7e3d", "mode": "video", "voice_id": "voice_2b7e1a9c" }'Listing and inspecting sessions
Section titled “Listing and inspecting sessions”List sessions on your account, optionally filtered by status:
curl "https://api.2wai.ai/v1/sessions?status=active" \ -H "Authorization: Bearer $TWAI_SECRET_KEY"Fetch a single session:
curl https://api.2wai.ai/v1/sessions/$SESSION_ID \ -H "Authorization: Bearer $TWAI_SECRET_KEY"Pausing without ending
Section titled “Pausing without ending”If a user steps away mid-conversation but you expect them back, park the session instead of tearing it down and re-creating it later:
curl -X POST https://api.2wai.ai/v1/sessions/$SESSION_ID/waiting_state \ -H "Authorization: Bearer $TWAI_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"waiting": true}'Send {"waiting": false} to resume active rendering. This keeps the session (and its session_id) alive and still counts against your concurrency limit (see Limits & concurrency), so end sessions you no longer need rather than leaving them parked indefinitely.
Reconnecting after a dropped WebSocket
Section titled “Reconnecting after a dropped WebSocket”A dropped WebSocket doesn’t mean a lost session. Ask for a fresh token and reconnect:
curl -X POST https://api.2wai.ai/v1/sessions/$SESSION_ID/token \ -H "Authorization: Bearer $TWAI_SECRET_KEY"async function reconnect(player, sessionId) { const res = await fetch(`/sessions/${sessionId}/token`, { method: 'POST' }); // your own backend route const { websocket_url } = await res.json(); player.connect(websocket_url);}Ending a session
Section titled “Ending a session”Always end sessions you’re done with. This frees the concurrency slot immediately instead of waiting for an idle timeout:
curl -X DELETE https://api.2wai.ai/v1/sessions/$SESSION_ID \ -H "Authorization: Bearer $TWAI_SECRET_KEY"Good times to call this: the user closes the chat/tab (via a beforeunload handler that fires a sendBeacon to your backend), your app’s own idle timeout fires, or the conversation is explicitly ended.
Session hygiene checklist
Section titled “Session hygiene checklist”- End sessions when a user leaves; don’t rely solely on idle timeouts.
- Don’t create a new session per message. One session covers an entire conversation. Reuse it and drive it with
/speak//audio. - Refresh tokens proactively (or reactively on
WebSocketcloseevents) rather than letting a stale token surprise you mid-conversation. - Watch
x-twai-concurrent-sessionson responses so you can back off before you’re throttled. See Limits & concurrency.
- Streaming into your app for the browser side of steps 2-3.
- Limits & concurrency for how many sessions you can run at once.