Skip to content

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.

  1. Create: POST /v1/sessions with avatar_id, mode, and optionally voice_id. Returns session_id, client_token, and websocket_url.
  2. Connect: the browser opens websocket_url and sends {"type": "session.ready"}. See Streaming into your app.
  3. Drive: your server calls POST /v1/sessions/{id}/speak (or /audio) whenever the avatar should talk, and POST /v1/sessions/{id}/interrupt to cut it off.
  4. Idle (optional): POST /v1/sessions/{id}/waiting_state parks the session in its idle loop without ending it, and resumes it later.
  5. Refresh (as needed): POST /v1/sessions/{id}/token mints a new client_token/websocket_url, for reconnects or before the current token expires.
  6. End: DELETE /v1/sessions/{id} when the conversation is over, releasing the session’s resources.
Terminal window
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"
}'

List sessions on your account, optionally filtered by status:

Terminal window
curl "https://api.2wai.ai/v1/sessions?status=active" \
-H "Authorization: Bearer $TWAI_SECRET_KEY"

Fetch a single session:

Terminal window
curl https://api.2wai.ai/v1/sessions/$SESSION_ID \
-H "Authorization: Bearer $TWAI_SECRET_KEY"

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:

Terminal window
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.

A dropped WebSocket doesn’t mean a lost session. Ask for a fresh token and reconnect:

Terminal window
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);
}

Always end sessions you’re done with. This frees the concurrency slot immediately instead of waiting for an idle timeout:

Terminal window
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.

  • 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 WebSocket close events) rather than letting a stale token surprise you mid-conversation.
  • Watch x-twai-concurrent-sessions on responses so you can back off before you’re throttled. See Limits & concurrency.