Transports
A transport determines how audio reaches the worker and how video gets back
to you. It’s a tagged union on the session’s transport field, keyed by type.
class TransportType(str, Enum): FAKE = "fake" # internal only — not a customer transport LIVEKIT = "livekit" # available now (BYO) WEBSOCKET = "websocket" # reserved, not yet available PIPECAT = "pipecat" # reserved, not yet availableLiveKit (BYO)
Section titled “LiveKit (BYO)”Protoface uses Bring Your Own LiveKit. You already own a LiveKit room; our worker joins it and publishes a video track. This is the same pattern every LiveKit Agents avatar plugin uses (Tavus, Simli, D-ID, Hedra, Beyond Presence, and others).
Request shape
Section titled “Request shape”{ "type": "livekit", "url": "wss://my-app.livekit.cloud", "room_name": "demo-room", "worker_token": "eyJ...", "worker_identity": "protoface-worker", "audio_source": "data_stream", "subscribe_to_identity": null, "synthetic_audio_if_no_input": false}| Field | Required | Notes |
| ----------------------------- | -------- | ------------------------------------------------------------ |
| url | yes | Your LiveKit server URL (wss://...). |
| room_name | yes | The room the worker joins. You create/own it. |
| worker_token | yes | A JWT you mint authorizing the worker to join room_name. |
| worker_identity | no | Identity the worker uses in the room. Default protoface-worker. |
| audio_source | no | data_stream (default) or track. See below. |
| subscribe_to_identity | no | When audio_source: track, pin a specific participant. |
| synthetic_audio_if_no_input | no | Demo aid — synthesize audio if none arrives. Off by default. |
Audio source
Section titled “Audio source”audio_source selects how the worker receives audio:
data_stream(default) — uses LiveKit’sDataStreamAudioReceiver. This is canonical for LiveKit Agents customers and what thelivekit-plugins-protofacepackage uses. It also gives you free interrupt semantics.track— the worker subscribes to a participant’s published audio track. Withtrack, setsubscribe_to_identityto pin a specific participant; ifnull, the worker subscribes to the first audio track it sees.
Minting the worker token
Section titled “Minting the worker token”You mint the token with your LiveKit server SDK and your own key/secret:
import { AccessToken } from "livekit-server-sdk";
const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, { identity: "protoface-worker",});at.addGrant({ roomJoin: true, room: "demo-room" });const workerToken = await at.toJwt();from livekit import api as lk_api
worker_token = ( lk_api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) .with_identity("protoface-worker") .with_grants(lk_api.VideoGrants(room_join=True, room="demo-room")) .to_jwt())Then pass it on session create — see the Quickstart and the Sessions guide.
Read shape
Section titled “Read shape”On GET, a LiveKit transport echoes back the same fields with the secret
redacted:
{ "type": "livekit", "url": "wss://my-app.livekit.cloud", "room_name": "demo-room", "worker_token": "********", "worker_identity": "protoface-worker", "audio_source": "data_stream"}Reserved transports
Section titled “Reserved transports”These are declared in the contract so we can add them without a breaking change.
They are not implemented yet — sending them today returns
400 invalid_request with code transport.unsupported.
websocket (later)
Section titled “websocket (later)”Customer-streamed audio in, server-streamed video out over signed, short-lived WebSocket URLs returned at session-create time. URLs are always returned by the server; clients never construct them.
pipecat (reserved)
Section titled “pipecat (reserved)”Reserved for a future Pipecat-native integration. No request shape is committed yet.