Skip to main content

Sessions and modes

A session is a server-issued realtime context (sessionId) that scopes rate limiting, conversation history, and audio routing. Every action — readAloud, sendBrowserAction, voice call, listener subscription — runs inside a session.

Modes

ModeUsed forAudio routing
textPure text chatNone
audio_playbackTTS / read-aloud / non-call voice repliesStreamed over the channel WebSocket
voice_conversationFull two-way WebRTC voiceSIP leg via the WebRTC gateway
listenInterpretation listener — subscribe to a TelPhi streamStreamed over the channel WebSocket
browser_actionsPure BOA dispatch with no AI conversationn/a

Find-or-create per endpointId + mode

The SDK keeps one session per (endpointId, mode) pair. Subsequent calls with the same pair reuse the existing WebSocket and the same endpoint can run multiple modes side by side (for example, a voice_conversation speaker and a listen subscriber on the same endpoint during interpretation).

// First call opens the session
await delphi.readAloud('Hello', {endpointId});

// Same (endpointId, mode='audio_playback') — reuses the WebSocket
await delphi.readAloud('Are you there?', {endpointId});

// Different mode — opens a separate session for the same endpoint
await delphi.startCall({endpointId, autoDial: true});

Migrating between text and voice

When a flow exposes both web_chat and web_voice entry points, TelAPI can move a live session between text and voice_conversation without losing the same sessionId or conversation context. Check runtime.migration from capability discovery before showing handoff controls:

const caps = await delphi.getCapabilities(endpointId);

const canUpgrade = caps.runtime.migration.includes('text_to_voice');
const canDowngrade = caps.runtime.migration.includes('voice_to_text');

Upgrade — text → voice

Requires an open text session first:

delphi.setRemoteAudioElement(remoteAudioEl);

await delphi.upgradeToVoice({
endpointId,
autoDial: true,
browserContext: {identifier: 'user-123'}, // optional
});

TelAPI serialises the text FlowEngine session, re-issues a voice_conversation token for the same sessionId, and returns WebRTC gateway metadata. The SDK closes the text WebSocket, opens voice, re-imports chat history for the UI, and connects WebRTC.

REST equivalent: POST /api/v1/sessions/upgrade — see Browser SDK → Text chat.

Downgrade — voice → text

Requires an active voice_conversation session:

const session = await delphi.downgradeToText({
endpointId,
endCallFirst: true, // hang up WebRTC before reopening text (default)
});

session.sendTextChat('Can you recap in writing?');

The SDK sends prepare_voice_to_text_handoff on the channel, calls POST /api/v1/sessions/downgrade, tears down WebRTC when endCallFirst is true, then reopens mode: 'text'.

REST equivalent: Browser SDK → Text chat → Downgrade.

When migration is unavailable

ConditionEffect
FEATURE_WEBRTC=falseVoice upgrade returns 503; text-only still works if interactionModes.text is true.
FEATURE_API_ACCESS=falseSession and capability routes are not registered.
Flow missing web_chat or web_voiceruntime.migration omits the corresponding direction; open the supported mode only.

Full walkthrough: SDK quick start → Text chat.

Text overlay on voice (not migration)

During an active voice call you can enable typed input without changing session mode: session.enableTextChat(), sendTextChat(), setResponseMode(). That is separate from upgrade/downgrade — see Text chat quick start.

Closing sessions

// Close just the audio-playback session
await delphi.endSession(endpointId, 'audio_playback');

// Close every mode for an endpoint
await delphi.endSession(endpointId);

// Close everything the client has open
await delphi.endAllSessions();
Mode-keyed lookups

getSession(endpointId) returns an arbitrary match when multiple modes are open. Pass getSession(endpointId, mode) whenever your app may have more than one mode active for the same endpoint.

Idle timeout

Non-voice sessions auto-close after sessionIdleTimeoutMs (default 300_000 = 5 minutes). The clock resets on every outbound send and every inbound message. Set the value to 0 to disable.

Voice sessions ignore the timeout — they live until you explicitly hang up via endCall().

Session state shape

Each SessionClient exposes a small state object suitable for useSyncExternalStore:

const state = session.getState();
// { sessionId, mode, connected, lastActivityAt, lastMessage, ... }

session.subscribe(() => {
// re-render on state change
});

The orchestrator-level state on DelphiClient aggregates this for all open sessions:

const state = delphi.getState();
state.sessions; // [{ endpointId, sessionId, mode, connected, lastActivityAt }]
state.voiceCall; // { inCall, calling, registered, telproDomain, ... }

Next step

Configuration — the DelphiClient constructor options that shape session and call behavior.