Skip to main content
FaceMode does not host or create your WebRTC room. You create a room in your own LiveKit or Daily account, hand FaceMode a credential that lets it join and publish, and the avatar appears as one more participant in that room. Your frontend subscribes to its avatar video and audio tracks like any other remote participant.

The room object

Every session carries a room object describing where FaceMode should publish:
The room type is locked to the session: a reconnect that passes a room with a different type is rejected with ROOM_TYPE_MISMATCH.

How the room name is resolved

For livekit rooms the name is resolved in this order:
  1. livekit_room_id on the create request body, if present.
  2. room.name, if present.
  3. The video.room claim inside the LiveKit token itself.
If none of these resolve for a LiveKit room, creation fails with ROOM_INVALID - in practice, minting the publish token with a room grant already satisfies resolution. For daily rooms an omitted name falls back to a generated identifier, since the Daily room is identified by its URL.
Legacy room fields are rejected outright. Sending byolr, livekitUrl, or livekitToken fails with ROOM_LEGACY_FIELDS - use the room object instead. The removed waitForIngestion field is likewise rejected with WAIT_FOR_INGESTION_REMOVED.

LiveKit rooms

Create the room yourself (LiveKit Cloud dashboard, RoomServiceClient, or your existing provisioning). Then mint two tokens with different identities:
Two things to watch:
  • Identity collision - if the publish token shares an identity with your agent or a viewer, LiveKit kicks the older participant. Always give the avatar its own identity.
  • Token lifetime - the token must stay valid for the whole session. Mint it with a TTL that covers the expected call duration; reconnect requests can carry a replacement room object with a fresh token if needed.

Daily rooms

For type: "daily", create the room through the Daily REST API and pass its URL as room.url. room.token is a Daily meeting token scoped to that room with permission to join and publish - your backend mints it with properties.room_name set to the room name. Viewers join with their own meeting tokens (or the room URL directly for public rooms) and subscribe to the avatar participant’s tracks.

What FaceMode publishes

Once the session is negotiated, the avatar joins as a single participant and publishes two tracks:
  • Avatar video - the 1024x1024 stream (VP8 or H264), carrying the idle loop, live lip-synced frames, and the crossfades between them.
  • Audio - the rendered speech track, published when speech is active.
Your frontend does not need FaceMode-specific code: join the room with the standard LiveKit or Daily client SDK, subscribe to the avatar participant’s tracks, and render the video element. The same track carries idle and live content, so no state switching is required on the viewer side.

Reconnects and credential rotation

POST /api/sessions/:id/reconnect accepts an optional replacement room object. Use it when the original room token is about to expire or you are moving the session to a different room instance. Constraints:
  • room.type must match the session’s original type.
  • inputProvider, if sent, must equal the value used at creation.
  • The response mints a fresh one-time wsToken for the new socket.

Credential checklist

1

Create the room in your own account

LiveKit RoomServiceClient or the Daily REST API. FaceMode never creates rooms for you.
2

Mint the publish token server-side

LiveKit: room_join + can_publish + can_subscribe on the room name, under a dedicated identity. Daily: a meeting token scoped to the room.
3

Pass it as room.token on session creation

It travels inside the session’s signed credential - keep it off browsers and out of logs.
4

Mint viewer tokens per user

Subscribe-only grants, one identity each. This is the only room credential that may reach a browser.
GET /api/sessions/:id echoes room.type back but never the URL or token - credentials are returned only in the create and reconnect responses.