@scenaro/sdk separates two things that most voice SDKs bundle together: the session contract your app codes against, and the realtime engine that actually moves audio and data. That boundary is called Transport.
You rarely need this page to ship a voice experience — useScenaroSession hides Transport completely for the embed path. Read this if you’re deciding whether code belongs in your app or in a Transport-specific extension, or if you’re curious how the SDK stays engine-agnostic.

Why a Transport boundary exists

Today, Transport is implemented with LiveKit — WebRTC audio plus a data channel for the agent ↔ frontend protocol. Tomorrow it could be Pipecat or another realtime engine. ScenaroSession never calls LiveKit directly — it calls an internal Transport interface, and a LiveKitTransport implements that interface underneath. This mirrors the internal boundary in the SDK source — packages/sdk/src/core/transport.ts:
This interface is not exported from @scenaro/sdk — it’s an internal seam, not a plugin API you implement yourself. What it buys you as a developer is a stable contract on top of it.

Two levels: portable contract vs engine extension

LevelWhat it isStabilityExamples
Portable contractScenaro’s session API, independent of the engine underneathStable across future Transport implementationsstatus, agentState, messages, config, tools, session events
Engine extensionThe current engine’s own API surfaceTied to LiveKit today — not guaranteed to carry overroom, RoomContext, @livekit/components-react hooks/components
Code written against the portable contract keeps working if Scenaro ever adds another Transport implementation behind useScenaroSession. Code that calls room.localParticipant... or uses LiveKit’s React hooks is written against the current engine directly — the same trade-off you’d make using that engine standalone.
See Advanced: Transport extensions for how to use the current engine’s extension surface.

What “transport session” means in the docs

Across the documentation, transport session refers to the realtime connection established by start() — audio (if enabled) plus the data channel carrying the protocol. The wire response from POST /v1/public/session is described as transport credentials: today those fields are named livekit_token and livekit_url (see ScenaroClient.startSession), because LiveKit is the current implementation. The field names are not renamed as part of this documentation alignment — only the prose around them is engine-agnostic.

Implementations

EngineStatusWhere
LiveKitCurrent, only implementationpackages/sdk/src/transport/livekit/
PipecatNot implemented — this boundary exists so it could be added without rewriting ScenaroSession

Next steps