Skip to content

Telephony Preview

Status: Current | Last reviewed: 2026-10-20

Use this page when changing AI Studio's expanded Telephony or diagnosing IVR, caller identity, and human-transfer tests. The test phone uses the selected contact-center connection's live routing rather than injecting an AI draft.

Call paths

Provider Preview entry Caller identity
Genesys Janus → Asterisk outbound SIP → configured BYOC trunk → dialed IVR number Optional ANI becomes SIP From and P-Asserted-Identity; the trunk must trust it
Five9 Existing emulator SIP route through its configured SBC Same ANI request; provider policy controls acceptance
Amazon Connect Existing StartWebRTCContact endpoint → selected contact flow → browser Chime audio ani and caller_id_number attributes; not a native customer phone endpoint

PhoneCallTest sends an explicit telephony call option to auth-context. That option bypasses the direct AI WebRTC/LiveKit branches and local AI preflight. It never attaches X-Pipeline-Id, X-Test-Pipeline-Id, a draft version, or an internal-leg signature to an external SIP call. Attaching those headers or signing a phone number as an AI extension would bypass the IVR or fail authorization.

The server resolves the selected tenant-authorized connection and returns X-Call-Source: standalone-emulator, X-Target-FQDN, and optional X-Caller-Id-Number. Existing extensions.conf sets caller identity before Dial creates the outbound channel, and carries the same ANI as P-Asserted-Identity. Setting caller identity only in the B-leg header handler is too late to replace the original SIP From identity. Numbers require an explicit + country code: numeric-only strings can match an internal extension route.

The auth provider owns both transports, so the phone's mute, DTMF, hangup, busy state, and recording track getters follow the active call. SIP updatingcall events generate a JSEP answer and update on the existing handle with no new tracks. Hold/resume and transfer must not reset the call or stop its recording. Hangup cancels pending SIP preparation and rejects late offer callbacks. Connect guards asynchronous SDK/microphone setup with a generation counter, releases late microphone grants after cancellation, and supplies the local and remote tracks to recording. The Chime SDK loads only when starting a Connect call.

Controls and discovery

Diagnosing intermittent audio

SIP Telephony Preview writes a JSON [PreviewAudio] console entry every five seconds while its call is active. It includes the browser conversation ID, actual microphone sample rate/channel count and processing flags, the selected ICE candidate/relay transport, and cumulative WebRTC audio packet, jitter, concealment and buffer counters. Compare counter deltas within one stream; jitter and round-trip values are seconds. A stream or peer-connection change can reset counters. Missing fields mean the browser did not supply them.

The logger reads existing tracks and getStats() only. It does not request a microphone, process audio, change playback or upload a recording. Device IDs, device labels, candidate addresses, ICE credentials and TURN URLs are excluded. Polling ends with the call, and late results from an older SIP attempt are discarded. Console entries use serialized JSON so copied logs retain values instead of collapsed Object placeholders.

Correlate a bad call and a known good call by their exact provider conversation IDs. In Genesys, compare the external PCMU stream with the internal/agent streams; discardedPackets includes jitter-buffer timing discards and is not a count of packets lost in transit. Call-wide totals do not locate the fault at transfer. Compare the provider's separate caller/remote recordings with a bounded passive packet capture at Asterisk before changing codecs, noise suppression or headset settings. Installing an audio hook such as MixMonitor can affect bridge optimization, so document it as a test variable rather than assuming a recorded and an unrecorded call use identical media handling. An AI Activity event-stream error is separate from the WebRTC media connection.

Setup controls

usePreviewTelephony loads its catalog only while Telephony is enabled. PreviewTelephonyControls uses PreviewControlWidget in the left canvas column, sharing Mock Caller's rounded shell. Its Integration selector and editable destination combobox show Genesys numbers with route/flow names, or Connect flow names. Searching and dropdown browsing share the same input. The list supports arrow keys, Enter, Escape, and bounded scrolling. These controls render outside the handset shadow root and remain visible but disabled during calling, connected and recording states. Manual SIP entry remains available when discovery fails. The emulator uses initialApp="home" and idleScreen="home_banner" in both direct and Telephony Preview; its normal start action still calls handleStartCall, which dispatches the selected external telephony transport. The in-call PhoneKeypad retains its 80px keys and normal DTMF handling. The left widget stays outside the phone/AI Activity recording composition.

Connection and destination settings use sessionStorage key preview.telephony.v1:<tenantId>:<userId>; stale connection IDs do not fall back to a different destination. Selecting a different connection clears the previous phone destination and chooses its configured default Connect flow. Mode state itself is ephemeral. The switch appears only in expanded voice Preview and is disabled during a call or while Mock Caller is enabled. The mock transport connects directly to the AI agent and does not support IVR simulation.

phoneLabel is an optional-in-storage display override in the same settings. Older saved settings hydrate it as an empty string. Empty or whitespace-only labels display the open agent's name. resolvePreviewTelephonyCall omits the label from both SIP and Connect call options; it never replaces the destination or enters provider metadata. The widget disables label editing during calls and recording.

Collapsing Preview clears every mode switch and ends an active telephony call. Integration, destination, flow and label settings remain in session storage. Recording actions and downloads live in PreviewRecordingControls alongside the Telephony widget, outside the capture. The Preview header stays mounted.

ANI comes from templateVariables.ani, shown with the existing variable editor in the left controls column. Legacy saved ANI never overrides the current Preview value. Connect receives other nonempty template variables as contact attributes; normalized ANI takes precedence over ani and caller_id_number. SIP still uses only its supported ANI override, without internal agent/template headers. Variable inputs are disabled while the call is active.

Genesys number discovery reads GET /api/v2/architect/ivrs with server-side OAuth client credentials. It selects state=active, requires an assigned open, closed, or holiday flow, flattens dnis, and retains normalized E.164 numbers. The dropdown searches number, route name, and available flow names locally. It does not assert that a schedule is open or that an agent is available.

The OAuth client needs routing:callRoute:view in the relevant divisions. Requests use the exact environments in genesys-regions.json, disallow redirects, share a 20-second deadline, and read at most 20 pages of 100 routes. The API reports truncation; it never follows a provider-supplied nextUri. Missing credentials or discovery permissions do not disable manual dialing. SIP preparation reads configuration without decrypting OAuth secrets, so discovery credential failures cannot block that path.

All four Preview endpoints require AIManager:RunAgents (agents:run for tokens) and tenant context:

Endpoint Purpose
GET /api/preview/telephony/integrations Active, supported connection metadata; no config or secrets
GET /api/preview/telephony/genesys-numbers?integrationId=… Routed Genesys numbers
POST /api/preview/telephony/sip-target Validate {integrationId,destination,ani?} and prepare the configured SIP target
GET /api/preview/telephony/conversation?sessionId=… Resolve the exact Genesys or Connect conversation for the current user and tenant's prepared call

The existing Connect endpoints remain /api/emulator/amazon-connect/contact-flows, …/webrtc-contact, and …/dtmf. See API contracts.

Amazon Connect entry paths

Confirmed against AWS documentation on October 2, 2026:

  • StartWebRTCContact creates the inbound browser audio contact and starts the supplied ContactFlowId. It has contact attributes and participant details, but no customer phone endpoint/ANI argument. This is the existing emulator path reused by Preview.
  • CreateContact supports VOICE only with initiation method TRANSFER and subtype connect:ExternalAudio. It is not the general browser-call start API.
  • A SIP-based path exists through the Amazon Chime SDK Voice Connector CONNECT_CALL_TRANSFER_CONNECTOR integration. AWS documents direct voice and metadata transfer between Connect and other voice systems, including IVR use, but marks it as gated: contact the AWS account team/Connect specialist. The analytics connector is a different integration and does not provide this call-routing path.

Enabling that connector is separate infrastructure work. This Preview feature does not provision it or advertise ordinary phone-number SIP entry for Connect. When native ANI behavior is required, validate the chosen telephony ingress and identity policy end to end instead of substituting a custom contact attribute.

For a flow that transfers browser callers through an existing Voice Connector, do not bind VoiceConnector.FromUser only to $.CustomerEndpoint.Address. The October 9 Allianz Preview failures reached Connect successfully, then failed the transfer with FromUser: "" and took the flow's disconnect branch. Setting Preview's ANI does not populate that native endpoint.

The separate Allianz - Producer Support flow now resolves trueserveCallerId before each producer transfer: a native endpoint beginning with + takes precedence, followed by $.Attributes.ani beginning with +, then the literal SIP identity anonymous. The transfer reads $.Attributes.trueserveCallerId. Preview already normalizes entered ANI; the flow's prefix check selects the source rather than authenticating the caller. This is a flow correction, not a global default ANI or a new Preview API field. See the Allianz flow correction and end-session attributes.

Salesforce transfers from Connect Preview

SCV inbound routing supports registered WEBRTC_API Preview contacts even though Connect supplies no native caller or dialed-number endpoints. The Amazon integration's administrator-owned config.preview_scv_routes maps an entry flow UUID to {contactCenterId, dnis}. Configure only flows intended to use Salesforce routing; unmapped flows retain normal Connect Preview behavior. The Allianz producer mapping uses flow b566a161-89fa-4e6c-b509-97610bf1aa19, contact center 04vDW0000000AkAYAU and DNIS +18668985617.

For a mapped flow, the WebRTC start endpoint requires a normalized ANI, derives DNIS from this mapping, and rejects an explicitly conflicting DNIS. Before calling AWS it checks the relay's actual contact-center instance, region, routing enablement and phone allowlist through the service-only POST /api/sfdc/routing/preview-check. An unavailable relay or invalid mapping fails before starting a contact. This does not verify customer identity.

After AWS returns its contact ID, Studio stores a six-hour routing binding alongside the existing Preview ownership record. The relay resolves it using GET /api/internal/preview/connect-routing?instanceId=…&contactId=…, protected by SCV_SERVICE_TOKEN. Lookup requires both records and rechecks the current tenant-visible, active integration and its configured flow mapping. Removing the mapping or expiring either record revokes the fallback.

SCV_TRANSLATOR_URL in AI Manager identifies the relay's internal HTTP(S) origin (http://localhost:3031 in Vanguard). The relay uses AI_MANAGER_INTERNAL_URL (or AI_MANAGER_URL) to call Studio. Both services need the same existing SCV_SERVICE_TOKEN; keep it in private environment configuration. These lookups refuse redirects and have five-second deadlines.

The relay uses one resolved caller/destination pair for its number allowlist, VoiceCall creation and Omni Flow request. Native endpoints take precedence; ordinary phone calls do not look up a Preview binding. Caller attributes alone cannot establish a binding. A Preview call still needs the dedicated inbound flow's contact-center assignment, and the mapped DNIS must still be allowed. Case context, duplicate-creation guards and cleanup remain on the existing SCV path. No new Salesforce object or alternate Omni Flow is required.

Primary references:

AI Activity and recordings

The uncorrelated Genesys AI Activity panel displays Waiting for call to connect to AI agent.

Genesys SIP preparation mints a random previewSessionId, owned by the authenticated user and tenant in Redis for six hours, and supplies it as X-Conversation-Id. Asterisk's capture-preview-conversation pre-connect subroutine reads PJSIP_RESPONSE_HEADER(read,x-inin-cnv) on the answered outbound leg. This is the Genesys conversation UUID used by the Genesys connector as the TrueServe ID. The subroutine validates both UUIDs and writes TSF_PREVIEW_<previewSessionId>; the caller's hangup handler clears that Asterisk global.

The authenticated BFF polls the internal ARI-controller endpoint for this exact key across configured Asterisk instances. It checks session ownership before reading, rejects conflicting provider IDs, registers the provider conversation with the phone authorization registry, and caches the binding for the original session lifetime. No caller-number/time matching or Genesys API credentials are needed. ARI_CONTROLLER_URL and ARI_CONTROLLER_INTERNAL_TOKEN must be configured in AI Manager. Unknown, expired, and other-user/tenant sessions return 404.

For Genesys, telephonyConversationId feeds only AI Activity's passive mode=follow observer; the primary phone must not activate an AI runtime from the outbound SIP session. AI Activity connects when the routed TrueServe agent creates its conversation and shows transcript, tool calls, and persisted Conversation Start/End workflows. Keep the resolved ID after hangup so end workflows continue updating. A new SIP call clears it. The provider's published agent runs even when Studio has a draft open. IVR and human-agent events outside TrueServe are not supplied by this stream. Five9 does not yet provide this AI Activity correlation.

Connect Preview sends preview: {smsDeliveryMode} to the existing WebRTC start endpoint. The server chooses a fresh idempotency token, records the returned ContactId with its user, tenant and integration, and returns previewSessionId and notificationSessionId. The correlation endpoint waits for a persisted TrueServe conversation in that tenant whose connect_contact_id or connect_initial_contact_id matches that exact contact. It follows the first AI conversation and retains it after hangup; the provider ContactId itself never activates an AI conversation.

For Voice Connector calls, ARI reads the server-owned preview:connect:<integrationId>:<contactId> record only after validating the inbound integration and pipeline tenant. It supplies the existing runtime SMS delivery and notification-session headers. A bounded wait covers an inbound leg arriving before the start response is registered; ordinary calls have no outstanding Preview start and do not wait. Caller-supplied delivery headers cannot select a Preview session.

With SMS off, authentication and multimodal messages use demo_notification and arrive in the preview phone's ContactId-scoped notification stream. With SMS on, the existing real-SMS behavior applies and ANI is required. The phone's transcript and multimodal surface use the correlated TrueServe conversation separately in mode=follow. They retain the server's phone authorization rather than binding the agent currently open in the editor. These delivery overrides currently cover the Voice Connector → ARI voice path; Connect KVS-only ingress does not consume them.

The same trusted contact record sets runtime call_source to ai-manager-telephony-preview, which persists conversations.is_test=true. The marker is runtime-only: ARI strips it from incoming call-source headers, keeps its original routing source, and does not grant draft configuration access. With passkey.simulate_in_preview=true, SMS off and an active test conversation, authentication emits auth_mode=preview_simulation and opens the mock ceremony inside the phone browser. Delivery mode alone is insufficient: the October 10 follow-up received its SMS inside Preview but opened a real passkey tab because the conversation still had is_test=false. Start a new call after this correction; existing real passkey links retain their meaning.

The October 10 agent 4873 incident exposed both missing pieces: its 13:46 UTC Connect call persisted normally, but Preview neither correlated AI Activity nor passed its simulated-SMS choice. Authentication recorded real_sms and sent to the customer's stored mobile ending 3123; no demo notification was created. The correction does not change the published agent or Connect flow.

Long start workflows can accumulate incoming audio before the Live adapter is built. The runtime now includes server-observed setup time in its initial audio buffer budget, preserving those frames and allowing the greeting to start. See GPT Live for buffering and recovery bounds.

Recording reuses the phone/AI Activity crop and MP4 conversion. Caller audio always passes through the recorder's Krisp noise-cancellation branch; remote AI/provider audio is mixed directly. This does not replace the live SIP/Connect tracks. Recording waits for cleanup initialization before starting the call. Telephony and Recording widgets remain in the left column, outside the capture, and the variable editor stays below the composition. Both audio tracks come from the active transport. The routing widget stays mounted but disabled during a call or recording, preserving the selection for the next test. Repack shared/phone-emulator after editing shared components; see phone packaging.

Genesys activity stays waiting

If the AI answers but Preview has no correlated conversation, first inspect the loaded dialplan:

docker exec tsf-local-asterisk asterisk -rx 'dialplan show capture-preview-conversation'
docker exec tsf-local-asterisk asterisk -rx 'dialplan show ai-manager-outbound'

The capture context must exist, and the outbound Dial must include U(capture-preview-conversation^${CONVERSATION_ID}). The outbound hangup extension clears TSF_PREVIEW_<sessionId>. Check this while diagnosing the actual call: an empty global after hangup is expected, while the Redis session retains a successfully captured binding.

On October 20, Vanguard had the capture routine in repository source but not in the active or injected Asterisk configuration. Clearway calls produced conversation/tool events, while their Preview sessions never received the provider conversation ID. Installing the capture hook, routine and cleanup in the active/injected dialplan and reloading restored the correlation path. An isolated live check verified Asterisk → ARI → Preview binding, phone stream ownership, retention after hangup and rejection of other users/tenants.

Follow the Asterisk reload procedure. dialplan reload reads /etc/asterisk/extensions.conf; copying only into /inject-configs does not activate a change. Keep the active file, saved template and injected file aligned. Start a new Preview call after repair: a completed call whose answer was never captured has no recoverable binding. Never substitute a recent call matched by ANI or time.

Verification

Unit coverage pins phone validation, active route filtering, tenant/user storage keys, API payloads, server-side credential handling, and pagination. Browser coverage checks the actual Preview with a simulated Janus provider: dial headers, DTMF, same-call transfer renegotiation, pending-call cancellation, remembered selections, recording layout, Connect flow attributes/errors, exact provider-ID observation, and session ownership/expiry. ARI tests cover UUID validation, multiple instances, ambiguity, and unavailable connections. Automated fixtures do not place calls to external numbers or human agents.

On October 2, 2026, an actual Vanguard Genesys call through agent 4463's published route resolved its SIP response ID, displayed start workflow actions, delivered the Denise greeting as browser audio and transcript, displayed the completed end workflow after hangup, and preserved ANI +16293034545 through the workflow's matched-account branch.

When the greeting plays but caller speech is missed, inspect the caller recording and media counters separately from the transcript. On October 6, agent 4463 v21 received intelligible quiet speech through Telephony with zero reported packet loss but produced no caller turn. Its publication had inherited a draft-only server voice-isolation filter, absent in v20. Version 22 disables that extra filter while retaining browser microphone processing. A native audio replay alone did not reproduce the same failure consistently; the browser/SIP/Genesys path is required for this regression. See the Takeda runbook for configuration and evidence.

Version 24 restores agent 4463's server filter at strength 50 after matched recording and live-agent checks. The full-strength replay altered a quiet opening; the reduced setting retained the tested words while reducing background level. A published browser Telephony test verified the greeting, the complete controlled request and an agent response. The per-agent krisp_viva.noise_suppression_level setting defaults to the previous 100 when absent; other agents' defaults are unchanged.

For a missing greeting, distinguish actual speech from output packet counts. The October 6 13:32 UTC call reached agent 4463 with no connector drops, but low-level startup microphone noise caused the runtime to consume its first greeting without sending it. The corrected initial-connection path preserves buffered audio before sending the enabled greeting; recovery remains separate.

For audio-quality comparisons, download the Genesys recording channels and align the caller channel with TrueServe's received-caller track. TrueServe's track is downstream of Genesys, not the original browser microphone. An October 6 controlled browser tap recovered the same complete phrase at the browser, Genesys and TrueServe checkpoints, with less than 1 dB spread in approximate speech RMS. Live captions still changed the opening words. Older paired recordings also preserved the reported appeal request and pause phrase despite incorrect live captions. This verifies those sampled words, not every microphone or call; independent transcription is supporting evidence, not ground truth. Private paired recordings and a standalone listening report are in .local-data/takeda-demo/genesys-audio-comparison-2026-10-06/.

For integration acceptance, use a known test route in the target tenant, check the ANI received by the IVR, press a keypad option, route to a staffed queue, and verify two-way audio through hold/resume and agent connection. Record the call and inspect the downloaded MP4. Provider reachability, identity policy, and staffed-agent routing need this live check.