For the complete documentation index, see llms.txt. This page is also available as Markdown.

Use multi-character sessions

Create one Live API room with multiple characters, map their media, switch the active character, and update the roster safely.

Create one Live API room containing multiple character instances and route each user turn to one active character. Character instances remain independently addressable, even when two entries use the same character ID.

To have several instances answer the same message at once, create the room with group_chat: true and address it with group-address instead of switching the active character. See Build a group chat.

Multi-character sessions are available only to accounts with the feature enabled. Your account's character and participant limits still apply.

Prerequisites

  • A Convai API key with access to the Live APIs and multi-character sessions

  • One or more character IDs that the API key can access

  • A stable, non-empty end_user_id for each human participant

  • A LiveKit client that can publish audio and receive remote audio tracks and data messages

Set LIVE_API_URL to https://live.convai.com before running the examples.

Create and join a room

Send an ordered characters array to POST /connect. The first entry becomes the initial active character. Repeating a character_id creates another independently addressable instance of that character.

curl --request POST "${LIVE_API_URL}/connect" \
  --header "X-API-Key: ${CONVAI_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{
    "characters": [
      { "character_id": "11111111-1111-4111-8111-111111111111" },
      { "character_id": "22222222-2222-4222-8222-222222222222" },
      { "character_id": "11111111-1111-4111-8111-111111111111" }
    ],
    "connection_type": "audio",
    "end_user_id": "learner-42",
    "shared_session_key": "safety-training-42",
    "max_num_participants": 2
  }'

Each characters entry in the response represents one character instance. This shortened response shows two entries; the complete response contains one entry for every requested instance, along with the standard /connect fields:

Join the returned LiveKit room using room_url, room_name, and token. Another human can join the same room by calling /connect with mode: "join", a new end_user_id, and exactly one room locator:

You can use shared_session_key instead of room_session_id. Do not resend characters when joining an existing room.

Map each character instance

Use these identifiers for different jobs:

Field
Use it for

character_id

Identify the Convai character definition. It can repeat within a room.

membership_id

Address one concrete character instance for targeting or removal.

participant_identity

Match that character instance to its LiveKit participant and media tracks. Treat the value as opaque.

character_session_id

Continue that character instance's conversation in a later session.

Build the media map from characters[].participant_identity. Do not match a LiveKit audio track by character_id, because cloned character instances share the same character ID.

Wait for a bot-ready message for every usable character instance before enabling interaction with it. Its data.about object identifies the ready instance:

Switch the active character

Only the active character handles the user's conversational input. Send interaction-target over the LiveKit data channel to switch the target:

A successful server-response returns the current target and a new route_epoch:

Store the returned epoch and send it as expected_route_epoch with the next target change. Set target_membership_id to null to clear the active target; conversational input is not routed to a character until you select another one.

Update the roster

Send character-roster-update to add or remove character instances without creating a new room:

Address removals by membership_id, not character_id. If you remove the active instance, set replacement_target_membership_id to a ready instance that remains in the room. The roster cannot become empty.

On success, the server-response includes added, removed_membership_ids, active_membership_id, route_epoch, and the new roster_epoch. Store both returned epochs. New instances emit character-status messages as they start and become ready, followed by their own bot-ready message. Removed instances emit character-removed.

Verify and troubleshoot

Before sending conversational input, verify that:

  • Every intended instance has a unique membership_id and participant_identity.

  • partial_dispatch is false, or your client has handled each failed entry using its provisioning_status and failure_code.

  • Every usable instance has emitted bot-ready.

  • Each remote audio track is bound through participant_identity.

  • The latest successful route_epoch and roster_epoch are used for later commands.

Symptom
Likely cause
Fix

Two copies of a character resolve to the same client object

The client maps by character_id.

Map each instance by membership_id, and map media by participant_identity.

A target or roster command returns an epoch error

Another accepted command changed the room state first.

Refresh the room by joining it again, then retry with the current epoch and a new command ID.

A character never becomes ready

Its provisioning failed or is still in progress.

Inspect provisioning_status, failure_code, and character-status; do not route input to it until bot-ready.

A join request is rejected

The room locator, participant limit, or account access is invalid.

Send exactly one locator, verify the room is active, and confirm the account limits.

Use a unique id for each new command. If delivery is uncertain, retry the same command with the same ID and unchanged payload so the server can identify the duplicate safely.

Next steps

Build a group chatMulti-character sessionsConnect APIClient-to-server messagesServer-to-client messages

Last updated

Was this helpful?