Skip to main content
Version: 0.9.19

Connect a browser application

Build a browser client that starts a text conversation without exposing your team's API key. Voice calls use the same credential boundary. The path is: prepare an endpoint, authenticate the user in your backend, obtain a session token, connect the SDK, verify the conversation, and close it deliberately.

Availability: from 0.9.18, your platform operator must enable Browser SDK (FEATURE_BROWSER_SDK=true); without it, the session routes and /ws/session do not exist. Your team needs an API key permitted to create sessions for its own endpoint. Voice additionally requires FEATURE_WEBRTC=true, configured TelPro gateway services, an allowed subscription state and a key that grants the WebRTC channel. Text sessions do not require WebRTC. Feature availability explains how these capabilities depend on each other.

The examples use SDK 0.1.5. The SDK is versioned independently of the platform, and 0.9.18 does not change the SDK contract, so SDK 0.1.5 works with 0.9.18. The SDK reference keeps its own version number. Capability discovery reports what the flow and the deployment allow; it does not check that gateway infrastructure or provider credentials are ready.

1. Prepare the endpoint and credentials​

Use an app and endpoint owned by the same team as the API key. In TelWeb, select the app, open its Flow Designer and inspect the endpoint attached to the intended flow. Copy the endpoint ID, not the app ID. For the configuration controls and their effects, use Application entry points.

Prepare a published/synced flow for the channel you intend to expose: web_chat for a text entry or web_voice for a voice entry. Test your flow's providers and credentials before debugging browser transport. A draft-only change is not evidence that the endpoint's runtime configuration has changed.

Ask the administrator responsible for team API access to provide:

InputWhat your integration needs
API domainTelAPI hostname, such as api.example.com; this is not the TelWeb hostname.
Endpoint IDEndpoint belonging to your API-key team and prepared for the intended interaction.
Server-side API keyCREATE_CALL_TOKEN scope for token creation and capability discovery. Keep it in your backend's secret storage.
Optional discovery scopeREAD_TEAM_APPS if your backend calls GET /api/v1/endpoints to discover the team's browser endpoints. A listing alone does not prove a usable flow.
Network access policyEndpoint origin/IP restrictions and the network path from your backend to TelAPI, plus browser access to the channel/gateway.

A missing API-key option, an unavailable API route, and a rejected endpoint are different problems. Resolve enablement, team access and key scope before retrying session creation.

2. Put token creation behind your backend​

Your browser calls your backend. Your backend authenticates the application user, verifies that this user may use the requested endpoint and mode, then calls TelAPI with the team key. The browser receives the session response and connects to the runtime. Delphi's team key does not replace your application's end-user authorization.

Implement these two same-origin proxy routes in your application. The paths in the left column are examples you own, not extra Delphi endpoints:

Your backend routeForwarded TelAPI request
POST /api/delphi/sessions/tokenPOST /api/v1/sessions/token, JSON { "endpointId": "<UUID>", "mode": "text" } or "voice_conversation".
GET /api/delphi/runtime/capabilities?endpointId=<UUID>GET /api/v1/runtime/capabilities?endpointId=<UUID>.

For both routes, validate the endpoint against the signed-in user's permitted configuration. Validate the requested mode; do not create an unrestricted proxy for every endpoint the team key can access. Apply your application's request-forgery protection and request limits. Add the key to the server-to-server request as X-API-Key; never copy a browser-supplied key into that request.

Return the upstream successful JSON without dropping fields. A session response includes sessionId, wsToken, wsTokenExpiresIn and expiresIn; voice also needs telproDomain and webrtcGatewayUrl. The token route returns a 300-second WebSocket-token lifetime and a 3600-second session lifetime. These values are not a guarantee that an active conversation lasts that long; runtime closure, permissions and idle behavior are separate.

Treat the session token as a credential too: do not cache proxy responses, place them in analytics, or log their values. Preserve upstream error status and structured details for your application's error handler, with a safe user-facing explanation.

Check restrictions at the correct hop. The token route checks the Origin header and request IP that reach TelAPI. With a backend proxy, these may be your backend's values, not the browser's. A non-empty origin allow-list rejects a missing Origin. Agree the intended trusted forwarding/network configuration with your operator; do not disable restrictions just to hide a proxy mismatch.

3. Configure the client and discover support​

Install the SDK version agreed for your deployment. The following code uses the 0.1.5 API:

import {DelphiClient} from '@ki-kombinat/delphi-client-js-sdk';

const endpointId = 'REPLACE_WITH_YOUR_ENDPOINT_UUID';
const delphi = new DelphiClient({
apiDomain: 'api.example.com',
sessionTokenUrl: '/api/delphi/sessions/token',
runtimeCapabilitiesUrl: '/api/delphi/runtime/capabilities',
});

const capabilities = await delphi.getCapabilities(endpointId);

Replace the hostname and endpoint ID. apiDomain is a hostname without https://; the SDK uses it for the secure WebSocket origin. The two proxy URLs move authenticated HTTP requests to your backend; they do not proxy the WebSocket. Your browser must be able to reach the configured TelAPI channel origin. Do not add apiKey to this browser configuration.

For a custom channel client, the SDK connects to wss://<api-domain>/ws/session?sessionId=<sessionId>&token=<wsToken>, using the returned session ID and URL-encoded WebSocket token. This is the channel credential, not the team API key. Keep the URL out of logs and support screenshots. Use the matching SDK 0.1.5 channel types and channel contract; connecting this socket does not establish voice media.

Use capabilities.interactionModes.text or .voice_conversation to decide which start action to offer. If a mode is unavailable, correct the endpoint/flow selection before starting it. Even an advertised mode can fail when deployment services or access prerequisites are missing; handle the token and connection errors below.

4. Start text sessions or voice calls​

Choose one path for the first integration test. Opening a session and receiving a token are not proof that a user message reached the flow.

Text conversation​

Open text mode, subscribe to its state, and enable Send only when both the channel and server are ready:

const session = await delphi.openSession({endpointId, mode: 'text'});

// Bind these values to your view. Rendering must treat message content as text.
function renderSession() {
const state = session.getState();
const sendEnabled = state.connected && state.serverReady;
const messages = state.messages.filter(
(message) => message.type === 'chat' && message.chat?.content,
);
// Update your Send button, connection indicator and message list here.
// Do not log tokens or conversation content as a substitute for rendering.
}

const unsubscribe = session.subscribe(renderSession);
renderSession();

function sendText(text: string): boolean {
const state = session.getState();
if (!text.trim() || !state.connected || !state.serverReady) return false;
return session.sendTextChat(text);
}

Call sendText from your Send action. A false return means the message was not sent; keep the input and show the connection state instead of pretending it was delivered. A true return confirms local transport submission, not an assistant answer. Display the received messages and lastError/connectionState as appropriate. Add a bounded connection timeout in your UI; if readiness never arrives, close the failed session and investigate before offering a new start.

openSession can return while connection is still in progress. Do not send the first message immediately after await openSession without checking readiness.

Voice call​

Attach an audio element and offer the start action from a user gesture. The page needs a secure browser context and microphone permission:

<audio id="remote-audio" autoplay></audio>
const remoteAudio = document.querySelector<HTMLAudioElement>('#remote-audio');
if (!remoteAudio) throw new Error('Remote audio element is missing');
delphi.setRemoteAudioElement(remoteAudio);

// Call from your Start call button, after checking advertised support.
async function startVoice() {
try {
return await delphi.startCall({endpointId, autoDial: true});
} catch (error) {
await delphi.endCall();
throw error;
}
}

Subscribe to delphi and read delphi.getState().voiceCall for call progress; use the returned session's state for the channel. Registration or a connected WebSocket does not prove that two-way audio works. Verify microphone capture, remote playback and an actual assistant response. Provide a visible Hang up action calling await delphi.endCall() and retain any unsubscribe function returned by delphi.subscribe for cleanup.

If the browser blocks playback, provide an explicit audio-enable interaction. If the token succeeds but voice cannot start, check gateway metadata, microphone permission and network/ICE connectivity before changing the flow.

5. Verify the result and close the session​

Use synthetic, non-sensitive content for the first test. Confirm each stage separately:

  1. Your own backend accepts an authorized user and rejects an unauthorized endpoint selection.
  2. The token proxy succeeds without exposing the team key in browser source, requests or storage.
  3. The session reaches connected and serverReady.
  4. A text submission receives the expected assistant response, or a voice call has intelligible audio in both directions.
  5. The expected app/endpoint and conversation appear in the platform's investigation view when that runtime produces them. Use the conversation review for that view, and Choose recording outputs if a transcript or audio file is expected. Recording selection and storage are separate from successful transport.
  6. Your Stop/Hang up action ends the intended session and your view stops showing it as active.

For the text example:

await delphi.endSession(endpointId, 'text');
unsubscribe();

For voice, call await delphi.endCall() and unsubscribe your view listeners. Close remaining modes explicitly when disposing the whole integration (await delphi.endAllSessions()); use this only when you intend to end all sessions owned by that client. Do not rely solely on navigating away or an idle timer. Non-voice sessions have a configurable idle timeout (default five minutes in SDK 0.1.5); voice sessions use a different lifecycle.

When a stage fails​

FailureWhat to check next
No session/runtime routeCorrect API hostname and FEATURE_BROWSER_SDK; a route can be absent when the feature is disabled.
401The backend sends a valid team key in X-API-Key; check rotation, expiry and active state. Do not repeat invalid credentials indefinitely.
403Required scope, API-key team versus endpoint team, origin/IP restrictions, or voice subscription limits. Read the returned code/message rather than assuming every 403 means the same thing.
404Endpoint identity/team visibility or wrong route. Listing an app ID is not selecting an endpoint ID.
409 with FLOW_NOT_CONFIGURED or PREPARED_FLOW_NOT_FOUNDAttach and sync the intended endpoint flow; creating more tokens will not configure it.
503 with WEBRTC_DISABLED or TELPRO_UNCONFIGUREDAsk the operator to enable/configure voice infrastructure. Retry alone cannot enable a feature.
429Inspect rate/quota/credential-jail details and any retry timing. Fix bad credentials or quota exhaustion before retrying.
Token succeeded; channel never readyBrowser WebSocket origin, TLS/network access, token expiry and runtime logs. A successful token request is only the first stage.
Voice channel works but audio failsBrowser permission/playback, gateway/TURN/ICE network path and media configuration; do not interpret channel connectivity as media success.

Keep UTC time, endpoint ID, selected mode, session ID where available, HTTP status and error code, and a request ID when supplied. Never include API keys, WebSocket tokens or unredacted conversation content in a support bundle. Logging and investigation helps an operator connect those identifiers to the appropriate evidence source.

Do not blindly replay token creation after a timeout: it can create fresh state. Reconcile the result and clean up an existing client session before starting another. An SDK error string may lose structured upstream details; keep those safely at your backend without retaining credentials.

Understand the browser channel​

The browser connects its session WebSocket to TelAPI, which routes supported messages to the session's runtime owner. Text/chat and control messages use this channel. Voice-call WebRTC media is negotiated separately: a connected channel does not prove working microphone or remote audio. Follow the text or voice startup procedure, and use session recovery when either connection is interrupted.

The channel handles chat, status/control, browser-action requests and action results; server messages also carry transcript and audio-playback payloads where supported by the flow. The audio payload contract has start, delta and done events; delta contains base64 audio bytes. Do not assume the complete audio arrives in one message. Flow capabilities, runtime configuration and the selected client determine which messages are useful; a schema member alone does not establish an enabled feature.

Text messages and idle cleanup​

In SDK 0.1.5, sendTextChat(content) constructs a chat message with direction: 'to_ari' and a chat payload containing role: 'user', intent: 'conversation' and responseExpected: true. Assistant chat travels back toward to_browser; render the received payload rather than treating local submission as an answer. For custom clients, retain the exact SDK/channel version and envelope instead of sending the content string alone.

Use session.setBrowserContext(...) for the SDK's explicit browser-context update, and sendContextUpdate(...) for a context-only message without requesting an AI reply. These are distinct from submitting a conversational user turn or invoking a browser action. Check the method's send result and verify the selected runtime consumes the intended context; do not treat browser text as authorization for an external configuration change.

Non-voice sessions use sessionIdleTimeoutMs on the DelphiClient, with a default of 300,000 ms in SDK 0.1.5. A value at or below zero disables that client timer. It resets on session state notifications, not solely on a human typing: inbound/outbound traffic and other client state changes can keep the session alive. Voice sessions do not use this automatic idle-close path. This timer is distinct from WebSocket-token expiry, server session lifetime and application retention. End the session explicitly when the user leaves the conversation; do not use the idle timer as a security expiry guarantee.

Keep client and protocol versions explicit​

Use the selected SDK's typed client rather than constructing an undocumented wire client. This guide uses the independently versioned SDK 0.1.5 reference; confirm the deployed platform/client combination with its owner. When investigating a mismatch, compare that exact SDK and platform source, retain bounded session/time/error identifiers, and omit tokens and conversation content from shared evidence. Do not use a newer protocol description as proof that an older client supports it. For configuration and startup failures, use the stage-specific checks.

Recover a browser session​

After network loss or a page reload, first distinguish a dropped channel, a reloaded page and an intentionally ended call. Reconnecting a WebSocket does not establish that the voice media, server conversation or a previously submitted action survived.

A temporary network drop​

Keep the existing client/session and show a reconnecting state. SessionClient defaults to automatic reconnect with a fixed 2-second delay, not exponential backoff, and a 30-second ping interval. It retries abnormal closure using the same session ID and WebSocket token. Normal close 1000, server code 4000 and codes 4001–4099 suppress this automatic retry. There is no built-in retry-attempt limit in this path; provide a visible stop/retry decision instead of leaving the user indefinitely waiting.

Observe connectionState, connected, serverReady and the operation's required capabilities. A socket open sets connected before a server-ready message arrives. Voice additionally needs the call/media state and usable audio. Do not enable “send” merely because the browser network indicator recovered, and do not create another session on each reconnect notification.

A rejected or expired token is not renewed by channel reconnect. Preserve a redacted error and ask your backend to reconcile the session. A deliberate new session uses the normal authorized token flow; do not silently treat it as continuation of the previous conversation. Repeating the same stale token or starting a duplicate conversation will not restore missing state.

Check token and server state​

A browser connection and session record, plus a resumable voice runtime, have different lifetimes:

ItemRecovery boundary
Browser channel tokenFive-minute JWT; a new WebSocket attachment validates expiry. An already-open channel is not forcibly ended at five minutes by this check.
Stored session recordInitially retained for one hour. Its existence does not make an expired token valid or prove a voice runtime is alive.
Paused voice runtimeConditional, process-local state with a default 20-second window. Requires resumability and retained provider/session state. Prepared FlowEngine calls fall back to full cleanup; this pause mechanism does not preserve those calls.

Do not call the session-token creation endpoint as a same-session refresh: it creates a new session ID. If the saved token is expired, stop automatic retries and resolve the old session's state before deliberately starting another conversation. A connected channel alone does not prove that the previous audio leg resumed.

Use the close reason as well as the code. 4003 can represent token/session validation failure or credential throttling; repeated retries cannot repair an expired token. 4000 — Superseded by new connection means another connection replaced this one within that TelAPI process. Keep one recovery owner per session; multiple tabs repeatedly reconnecting can displace each other.

Message replay requires a retained Redis stream ID, carried as streamId, rather than an ordinary message ID. The explicit reconnect replay reads at most 100 entries at a time, and browser stream retention is bounded. Do not assume every missed event returns or that delivery is exactly once. Reconcile the visible conversation and use the SDK's documented state rather than re-sending actions blindly.

Voice reconnection reattaches media separately. Reusing the same call ID can recover an eligible paused session only where its in-memory runtime still exists. Process loss, a different receiving instance, expired cleanup state or an expired channel token can prevent recovery. A new session is a new conversation, not proof that the old call has ended.

For an unresolved failure, record the session ID, endpoint, time, SDK state and close code/reason without tokens. Ask the operator to correlate channel validation/replay messages and TelPhi pause/resume/cleanup events using the log guide. Verify both conversation state and actual media before declaring recovery complete.

A page reload during a voice call​

  1. Initialize one DelphiClient and attach your state observer and audio elements once.
  2. Call restorePersistedCall(). It reads a saved descriptor; it does not contact the server, validate a token or prove a call is alive. A missing/unreadable/stale descriptor returns null: show a stopped state and offer a deliberate new start only after reconciling the previous session.
  3. If a descriptor exists, attempt reconnectCall(descriptor) once. It supports only voice_conversation and requires the saved telproDomain, webrtcGatewayUrl and wsToken. Do not reconstruct missing credentials from UI labels or copy another user's storage.
  4. Continue observing state. The method can return while gateway/SIP registration is still pending; its fulfilled promise is not a “call resumed” confirmation. Media setup can also catch an error and expose Reconnect failed: ... through state rather than reject the outer promise.
  5. Confirm channel readiness, the intended session/endpoint and actual voice playback/input before allowing more work. Microphone permission and audio autoplay may need user interaction again. On failure, show a clear stopped/recovery-failed state, clean up deliberately and reconcile the previous session before offering a new call.

Do not call reconnectCall repeatedly from rendering or competing mount effects. It creates a new channel client and replaces the endpoint/mode map entry; that is not an idempotent cleanup guarantee for an earlier recovery attempt. Remove UI subscriptions on unmount and ensure only one owner controls recovery.

Local age check: SDK 0.1.5 rejects a saved descriptor when Date.now() - startedAt exceeds 20 seconds. During a calling/active voice call, a timer refreshes that timestamp every 5 seconds while a usable descriptor remains. This is a browser-side age check from the last stored timestamp, not a guaranteed server resume window measured from network loss. Background timer throttling, unavailable storage and server termination can prevent restoration sooner.

Choose the correct stop or cleanup action​

ActionConsequence
session.disconnect()Legacy channel disconnect: clears local timers/playback and session identity without invoking the parent close callbacks. It is not a voice hangup API; prefer close() for final session cleanup.
await session.close()Permanently closes that channel object, rejects pending audio waiters, clears its saved stream cursor and invokes close callbacks. Do not reuse a closed object.
await delphi.endSession(endpointId, mode)Closes the tracked session for that endpoint/mode. Omitting mode closes all tracked modes for the endpoint.
await delphi.endCall()Intentional voice hangup and tracked voice-session closure. Use this for the user's End call action, then verify the resulting stopped state.
delphi.cancelReconnect()Clears the pending recovery and saved voice descriptor and cleans up local media. It does not send SIP hangup and does not by itself close every channel; do not label it “remote call ended.”
delphi.destroy()Client teardown on final ownership removal; stops timers, tears down WebRTC and starts closing tracked sessions. Do not use destruction as a temporary network-retry mechanism.

Local cleanup is not evidence that a remote action or call has stopped. After an ambiguous disconnect, reconcile server state before starting duplicate work.

Preserve delivery uncertainty and protect saved state​

Channel recovery can request missed server events using a saved stream cursor. It is not a durable browser outbox, a restored full transcript or an exactly-once guarantee. The SDK records stream IDs and dispatches received messages without a general duplicate-ID filter. Make application-side action handlers safe against duplicate delivery using stable operation identities and server-side reconciliation where appropriate.

sendChat() can append an outgoing item to local message history before socket send succeeds. Its false return means the channel did not send it; true only means the WebSocket accepted the send locally, not that the backend completed the action. Label pending/failed/confirmed states separately. Do not replay chat messages, configuration actions or tool results automatically after an uncertain outcome.

Voice recovery stores delphi-voice-session-state in sessionStorage, including endpoint/session metadata, the refreshed startedAt, gateway addresses and the WebSocket token. The channel cursor is stored separately as channel_last_stream:<sessionId>. This descriptor does not contain the full transcript/audio or team API key, but it does contain a credential. Same-origin scripts can access it; do not export it, include it in screenshots/support bundles or enable verbose SDK logging without reviewing its sensitive output. Browser storage is best-effort, not a secure credential vault or cross-device resume mechanism.

Verify recovery with synthetic traffic for your actual SDK/platform combination: transient drop; reload during a call; denied microphone; expired token; missing/stale descriptor; canceled recovery; duplicate events; and an action whose result was lost. Keep the existing SDK reference and logging investigation available, but exclude tokens and conversation content from diagnostic evidence.

Move between text and voice​

Mode handoff changes a session's runtime path. It is separate from typing a message during an existing voice call. Begin with a working text/voice integration and check the endpoint's advertised runtime.migration capabilities. The backend that owns your API key must authorize the caller and proxy the upgrade/downgrade operations; implementing token creation alone does not enable handoff.

DirectionBackend requestSDK 0.1.5 method
Text to voicePOST /api/v1/sessions/upgrade, body {sessionId, endpointId}delphi.upgradeToVoice({endpointId, autoDial: true})
Voice to textPOST /api/v1/sessions/downgrade, body {sessionId, endpointId}delphi.downgradeToText({endpointId})

Both operations require the appropriate API authorization and CREATE_CALL_TOKEN scope. Keep the long-lived key on your backend. The SDK derives proxy URLs from a sessionTokenUrl ending in /token; implement and protect the matching /upgrade and /downgrade routes. Return the complete successful token response, including voice gateway metadata on upgrade.

Upgrade a connected text session​

  1. Keep the existing text session connected for the intended endpoint. The SDK rejects an absent or not-yet-identified session rather than creating a replacement text conversation automatically.
  2. Call upgradeToVoice. autoDial defaults to false; use true only when the user intends to begin the voice call. Apply the same microphone permission, audio-element and gateway preparation as starting voice.
  3. On a successful API response, the SDK closes the text channel, opens the voice session using the returned token and imports the previous message list into the new client view. Replace subscriptions bound to the former SessionClient with subscriptions on the returned instance.
  4. Verify the channel connection, gateway registration, actual call and a controlled voice turn. A fulfilled upgrade request is not proof that WebRTC connected or audio worked.

Return voice to text​

The SDK requires a connected voice session for that endpoint. It sends prepare_voice_to_text_handoff, waits briefly, then requests downgrade. By default endCallFirst is true; despite that option's name, the SDK makes the downgrade request before hanging up the voice leg. It then replaces the voice channel with a text SessionClient and imports the prior client message list.

Use the returned text SessionClient and verify a new text turn. Do not call endCall() first as a generic replacement for the handoff procedure: ordinary end-call cleanup can end the session you intended to migrate. endCallFirst: false changes hangup behavior and needs deliberate lifecycle handling; it is not the normal “return to chat” procedure.

Both routes retain the session ID and issue a fresh WebSocket token. The server attempts runtime-state migration; the downgrade path can fall back to persisted state when live migration state is unavailable. Retaining an ID or importing the browser's message list does not prove every in-flight action, transcript event or runtime variable survived. Verify continuity with controlled input, and avoid repeating an uncertain external action automatically.

Diagnose a failed handoff​

  • 404: when Browser voice is disabled, upgrade and downgrade routes are absent. Otherwise confirm the endpoint and whether the session still exists; a closed/expired session needs a deliberate new-session decision.
  • 409 INVALID_STATE: check the current mode, matching endpoint and migration result. Do not blindly retry a request after the server may already have changed mode.
  • 503 TELPRO_UNCONFIGURED on an available upgrade route: ask the operator to configure the voice gateway; changing the API-key scope does not provision a gateway. A new voice-token request can return 503 WEBRTC_DISABLED when Browser SDK is enabled but Browser voice is disabled.
  • A permission or subscription rejection: reconcile the team, allowed scope and account/plan condition.
  • A failure after a successful HTTP response: inspect the current server/client state and gateway setup before retrying. The original channel may already be closed.

Type during a voice call​

Keep the voice_conversation session when the user needs typed side input without changing modes. On its SessionClient, use enableTextChat(responseMode), disableTextChat() or setResponseMode(responseMode), where response mode is text, voice or both. enableTextChat() defaults to text in SDK 0.1.5. These methods send channel control messages; they do not perform text/voice session migration.

The methods return whether the SDK sent the message. The client can update its local text-chat state before receiving server confirmation. Observe status messages such as text_chat_enabled, text_chat_disabled and response_mode_changed, then verify the actual reply and audio behavior for the selected provider/channel. A true return value is not proof that the AI already applied the requested mode.

Later browser capabilities​

Use the mode-handoff procedure for migration and voice text overlay for typed input during an existing voice call.

For methods and types beyond this starting journey, use the SDK 0.1.5 core reference. For request and response shapes, use the API description your deployment serves, as explained in API description by capability profile, and runtime capabilities and voice. A deployment acceptance test should cover your actual SDK/platform versions, browsers, network path and enabled modes before release.

Browser action capabilities on the canvas​

Select the browser Actions surface to open Actions / Capabilities. Read aloud, Transform & read and Listen create a new capability of that type; multiple capabilities may share a type or message family. Add enables the authored actions channel, creates a unique internal ID/identifier, compiles the flow and re-runs layout. Clicking a capability row selects it; its delete button removes it without selecting it first.

These are capability projections, not new ordinary executable-node discriminators. Read aloud compiles to a TTS side flow. Transform & read compiles to an LLM-query step followed by TTS. Listen does not compile to that ordinary side-flow node sequence: it depends on the separate interpretation-listener runtime. Adding its card alone does not provision interpretation production, storage or browser playback.

Fields shared by action capabilities​

ControlWhat to configure
LabelHuman-readable title; empty removes the custom label. This does not change the internal action ID.
Browser message typeEvent family sent by the browser/runtime. Empty restores the action-type default.
IdentifierStable selector (slug) for the client/voice integration. Changing it changes what callers must select; keep client configuration aligned.
Input text templateRead aloud defaults to {{context.trigger.text}}; transform shares that initial template; Listen starts with {{context.trigger.identifier}}. Only the runtime path that consumes a template establishes its meaning.
Active voice behaviorInterrupt voice while action runs or Run in parallel. This controls side-flow isolation/host behavior, not a separate context namespace.
Allow Browser Voice to invoke this actionExposes eligible capability metadata to voice tooling where the runtime supports it. It does not authorize arbitrary external actions.
Continuation promptVisible only when voice invocation is enabled; optional text used by the host/model continuation path after the action. Clearing removes it.

New Read aloud and Transform & read use interrupt behavior and enable browser and voice invocation. Listen uses parallel behavior and disables voice invocation. The specialized inspector has no separate general enabled/browserAction.enabled switches; those definition fields should not be confused with the visible voice-invocation toggle.

When multiple capabilities share a message type, compiled selector conditions distinguish identifiers; the first sibling provides the missing-selector fallback. With a single sibling the compiled side flow does not add that selector filter. Send the intended stable identifier explicitly and verify the actual integration dispatch instead of relying on order as an access-control mechanism.

Transform & read​

ControlBehavior
PresetSimplify and read, Shorten and read, Explain and read, or Custom transform. Selecting the preset updates the preset value; it does not automatically replace the existing prompt.
Transform promptActual stored prompt used by the generated LLM-query step when browser override is disabled. New capability starts with a generic simplification prompt and Custom preset.
Allow browser prompt overrideDefault off. When on, the compiled step reads context.trigger.prompt instead of the stored prompt, not as a guaranteed fallback. Verify permitted client payloads and missing-prompt behavior.
Use as interpreted stream producerDefault off. When enabled, shows Target language and On error. This requires the separate interpretation-production integration.
Target languageInitially seeded en when interpretation settings are first created; must agree with the intended producer/listener stream.
On errorEmit error item, Retry or Skip; missing policy defaults emit_error. Verify the specialized producer's behavior rather than applying it to every generic webhook/LLM error.

Interpretation configuration also carries original/translated-text storage and cache policy defaults, but those are not additional fields in this inspector. Provider override, result variable and stream/storage settings likewise require the applicable source/configuration surface rather than an invented control here.

The generated ordinary transform path uses the stored prompt (or browser prompt when enabled), then speaks the mapped LLM result. Merely changing Input text template does not rewrite that prompt in the compiler. Also, the LLM runner can emit complete after an error, so a generated next step is not proof the transform succeeded. Review the node reference and data/transition rules, then inspect the conversation evidence.

Example: add two Transform & read capabilities, give each a stable distinct identifier and explicit prompt, and send synthetic text using the exact identifier and message type. Confirm the prompt used and returned audio before enabling voice invocation. Test an absent identifier and missing prompt separately; do not publish based on the card labels alone.

Listen​

ControlBehavior
Target languageNew action en; identifies intended interpretation language.
Start modeClosest to now, Live only, From beginning or From sequence. New action Closest to now.
Latency offset (ms)New action 0; edits use numeric conversion with invalid/empty fallback 0. A saved number is not measured delivery latency.
Include captionsNew action on.

The definition also seeds fallbackWhenMissingAudio:text_only and catchUpPolicy:skip_to_latest; they are not visible fields here. The listener message path requires an identifier, target language and scope from its request, and supports a stream cursor. Selecting From sequence does not expose a sequence/cursor input in this inspector. Verify the caller supplies the required request metadata; do not assume all saved Listen fields are automatically transmitted or consumed by every SDK path. A listener without a working matching producer and stored stream has nothing to replay.

Ordinary side-flow inspectors and deletion​

When the selected object is an ordinary side flow rather than a compiled action capability, the fallback panel exposes Side-flow name, Browser message type for WebSocket triggers, and Isolation mode (Parallel/Interrupt). Blank name becomes its ID; blank WebSocket message type falls back to browser.action.readAloud. A trigger card is not an executable conversation step.

Selecting a node inside a side flow opens the normal node inspector with a Side flow context label and destinations limited to that side flow. Read the main-versus-side-flow execution differences before using transition mappings or tool matching. Some generated action-node delete actions delete the owning capability, not just a single visual card. Capability editing/deletion recompiles the authored capability set; preserve intended source edits and verify the resulting generated node set afterward.

Deleting the final action does not automatically reset actionsEnabled to false. Check the channel configuration separately if you intend to remove the browser surface. These changes affect working configuration; they do not undo completed actions, delete remote effects or certify that an already-running session adopted the new graph. Use the application lifecycle and endpoint reconciliation procedure for activation and recovery.

For the backend credential used to create sessions, follow Manage team API keys. Key rotation and deletion are separate from ending an existing browser session.