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 + transport: "livekit". Returns session_id, livekit_url, participant_token, room_name, agent_identity.
  2. Connect: the client joins the LiveKit room using livekit_url + participant_token. The agent appears as another participant; subscribe to its video + audio tracks. 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. Reconnect (as needed): LiveKit SDKs handle reconnect internally; on long-dropped sessions your mint proxy may re-mint via POST /v1/sessions to get a fresh participant_token.
  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_441ad4d95906438490a85b79",
"mode": "video",
"voice_id": "vc_e72e888d566aca3a4e63fbe8",
"transport": "livekit"
}'

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.

Reconnecting after a dropped LiveKit connection

Section titled “Reconnecting after a dropped LiveKit connection”

LiveKit SDKs handle short network blips automatically — the SDK reconnects to the same room with the same participant_token without any help from your mint proxy. For longer drops (token expired, room closed, server-side session ended), mint a new session and have the client re-join the fresh livekit_url + participant_token:

async function reconnect(mintProxyURL) {
const res = await fetch(mintProxyURL, { method: 'POST' }); // your own backend route
const { livekit_url, participant_token } = await res.json();
await room.disconnect();
await room.connect(livekit_url, participant_token);
}

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.
  • Let your LiveKit SDK handle reconnect for short blips; re-mint via POST /v1/sessions for longer drops rather than letting a stale token surprise you.
  • Watch x-twai-concurrent-sessions on responses so you can back off before you’re throttled. See Limits & concurrency.