Text chat
Delphi supports pure text chat in the browser through the same TelAPI session surface as voice — without WebRTC, microphone access, or a SIP leg. The client opens a session with mode: 'text', connects to /ws/session, and exchanges chat messages on the WebSocket channel.
During an active voice session you can also send text with enableTextChat / sendTextChat on the same channel. That keeps one voice_conversation session and toggles whether the AI processes typed input. This page covers the dedicated text session mode (web_chat flow entry) and session migration between text and voice. See SDK → Text chat quick start for both patterns.
Prerequisites
| Requirement | Why |
|---|---|
webrtc feature flag on | Gates all client-facing TelAPI session routes, including text. |
Endpoint with a web_chat entry point | Text mode runs the flow's web-chat path, not the phone or web-voice path. |
API key with CREATE_CALL_TOKEN | Required for POST /api/v1/sessions/token, upgrade, and downgrade — same scope as call tokens. |
| For text ↔ voice handoff | The flow should expose both web_chat and web_voice entry points. TelAPI advertises text_to_voice and voice_to_text in runtime capabilities when both are present. |
Open a text session
1. Request a session token
POST /api/v1/sessions/token
X-API-Key: <team-api-key>
Content-Type: application/json
{
"endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f",
"mode": "text"
}
Response (no WebRTC fields):
{
"sessionId": "call-loc5q7g0-k7p3f9x2",
"wsToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"wsTokenExpiresIn": 300,
"expiresIn": 3600
}
Exact shapes are in the API reference under Sessions → Request a session token.
2. Connect the WebSocket
wss://<api-domain>/ws/session?sessionId=<sessionId>&token=<wsToken>
The server sends an initial status message with state: connected. All further traffic uses the ChannelMessage envelope — see SDK → Channel protocol.
3. Send and receive chat
| Direction | Message | Purpose |
|---|---|---|
| Client → server | type: chat, direction: to_ari | User message; set chat.intent: 'conversation' and responseExpected: true for a normal turn. |
| Server → client | type: chat, direction: to_browser | Assistant reply (chat.role: 'assistant'). |
| Client → server | type: chat, intent: 'browser_context', responseExpected: false | Durable page context without triggering a reply. |
The client SDK wraps this as sendTextChat() / onChat on a SessionClient opened with mode: 'text'.
Session idle timeout
Non-voice sessions (including text) auto-close after the SDK's sessionIdleTimeoutMs (default 5 minutes). Every inbound or outbound message resets the clock. Voice sessions ignore this timeout. See SDK → Sessions and modes.
Upgrade text → voice
When the user moves from typing to speaking on the same conversation, TelAPI migrates the active text session and re-issues a voice_conversation token for the same sessionId. FlowEngine state is serialised to Redis so TelPhi can resume context when the WebRTC leg connects.
POST /api/v1/sessions/upgrade
X-API-Key: <team-api-key>
Content-Type: application/json
{
"sessionId": "call-loc5q7g0-k7p3f9x2",
"endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f"
}
Success response includes telproDomain and webrtcGatewayUrl in addition to a fresh wsToken. The client should:
- Close the text WebSocket (or let the SDK replace the session entry).
- Reconnect
/ws/sessionwith the new token. - Initialise WebRTC against the returned gateway and dial the SIP leg.
SDK: delphi.upgradeToVoice({ endpointId, autoDial?: true, browserContext? }) — requires an open text session for that endpoint. See SDK quick start → Upgrade to voice.
| Error | Typical cause |
|---|---|
404 NOT_FOUND | Session expired or unknown sessionId. |
409 INVALID_STATE | Session is not an active text session for that endpoint, or migration failed. |
503 WEBRTC_DISABLED / TELPRO_UNCONFIGURED | Platform cannot place browser voice calls. |
Downgrade voice → text
After the WebRTC leg ends (or is about to), TelAPI re-issues a text token for the same sessionId so chat can continue with persisted history.
POST /api/v1/sessions/downgrade
X-API-Key: <team-api-key>
Content-Type: application/json
{
"sessionId": "call-loc5q7g0-k7p3f9x2",
"endpointId": "0f6c3d4e-8b8f-4c59-9d0e-7e5e3b1f7f0f"
}
SDK: delphi.downgradeToText({ endpointId, endCallFirst?: true }) — requires an active voice_conversation session. The SDK sends a prepare_voice_to_text_handoff control message, calls the downgrade endpoint, hangs up WebRTC when endCallFirst is true (default), then opens a new text session with the returned token and re-imports the message list for UI continuity.
Capability discovery
Before showing “Start chat”, “Call”, or handoff buttons, query what the endpoint supports:
GET /api/v1/runtime/capabilities?endpointId=<uuid>
X-API-Key: <team-api-key>
| Field | Use |
|---|---|
interactionModes.text | Whether mode: 'text' is allowed. |
interactionModes.voice_conversation | Whether WebRTC voice is allowed. |
flows.entryPoints | Expect web_chat for text-only; both web_chat and web_voice for handoff. |
runtime.migration | Includes text_to_voice and voice_to_text when the flow supports both web entry points. |
The SDK exposes this as delphi.getCapabilities(endpointId) — see SDK → Capability discovery.
Text overlay during an active voice call
If you only need typed side input during a call — without changing session mode — keep the voice_conversation session and use channel control messages:
| Control | Effect |
|---|---|
enable_text_chat | AI processes typed chat messages (optional responseMode: text, voice, or both). |
disable_text_chat | AI returns to voice-only input. |
set_response_mode | Switch AI output modality without closing the call. |
SDK: session.enableTextChat(), session.disableTextChat(), session.setResponseMode(). Server acks arrive as status states text_chat_enabled / text_chat_disabled / response_mode_changed.
This does not replace upgrade/downgrade — it is the in-call typing UX on a single voice session.
Recommended integration path
Use the client SDK:
const session = await delphi.openSession({endpointId, mode: 'text'});
session.sendTextChat('Hello');
// optional: await delphi.upgradeToVoice({ endpointId, autoDial: true })
Building without the SDK? Follow the REST + WebSocket steps above and use the SDK source as the reference implementation for message shapes.
See also
- Call tokens — auth model and
CREATE_CALL_TOKENscope (shared with session tokens). - WebSocket channel — control plane shared by text and voice.
- SDK → Text chat quick start — end-to-end SDK recipe including React
useDelphiSession. - SDK → Sessions and modes — session map, idle timeout, migration overview.
- Conversations — how sessions surface in TelWeb after the fact.