Skip to content

Live Genesys transcripts in a host app

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

Use this guide to attach a host app to the transcript of a human-to-human Genesys call. The existing AudioHook connector, transcription-only Copilot, conversation API and surface embed support this architecture. The external phone's SIP path still has integration gaps; do not promise a complete embed-token-only Genesys test phone from the current phone quickstart.

This review covers the working source on environment/vanguard, including local changes. It is not evidence that official Dev or QA runs the same revision, or that a particular Genesys organization has been configured.

What is available

Component Current support
Copilot with no active workers Supported; keep copilot.types: [] and a configured streaming transcription stage
Live caller and human-agent audio Genesys AudioHook supplies external/internal audio channels to the connector
Answer in Genesys WebRTC Use Genesys's agent softphone or its WebRTC SDK; this is separate from the TrueServe phone
Real customer phone Dial the Genesys number whose flow/queue is configured for the AudioHook integration
Authenticated TrueServe test phone Studio Telephony Preview and the standalone emulator have Genesys SIP routing
External dynamic phone /api/v1/emulator/sessions supports direct TrueServe WebRTC, not Genesys SIP destinations
External stored SIP phone config Config and UI exist, but authentication and target-resolution gaps remain below
Embedded transcript and raw events TrueServeEmbed.create() and its copilot-event callback
Headless transcript consumption @trueserve/sdk, connect(id) and subscribeToEvents(callback)

The media and event paths are:

Real phone or authenticated test phone
  → Genesys number / flow / queue ↔ human agent's Genesys WebRTC softphone
  → Genesys AudioHook → genesys-connector-go → transcription-only Copilot
  → conversation-api event stream → surface-app embed or @trueserve/sdk

Subscribing to a conversation does not start audio capture. Configure the Genesys AudioHook tap for the human-agent portion of the call. Routing a caller to an AudioConnector voicebot alone does not establish that human-call tap.

Configure the transcription-only Copilot

Create a Copilot using the Custom engine and select a supported streaming transcription service with a working tenant integration. Keep its real STT stage in config.pipeline. Configure the following top-level branch:

{
  "agent_type": "agent_assistant",
  "copilot": {
    "engine": "custom",
    "types": [],
    "transcription": {
      "enhancement": { "enabled": false }
    },
    "gate": { "enabled": false },
    "translation": { "enabled": false },
    "summarization": {
      "on_transfer": { "enabled": false },
      "post_call": { "enabled": false }
    }
  }
}

This is a config excerpt, not a complete agent import. Removing the workers alone does not disable enhancement, the gate, translation or summary models. Remove unwanted lifecycle workflows as well if the agent should only transcribe. Keep settings.compliance.disable_transcript_storage off: that setting suppresses transcript publication as well as storage.

Publish the agent and configure the Genesys integration's AudioHook pipeline mapping to select its base agent ID for the test number. Inspect the resolved mapping; an existing rule can select a different agent than the URL suggests. For any CCaaS SIP ingress into TrueServe, also select the integration in the agent's publish-time CCaaS availability list.

Configure AudioHook to send both external and internal audio. A source that only supplies the caller channel cannot produce the human-agent transcript. The runtime supports an empty worker list and builds the transcript publisher independently of those workers. See Copilots and the Genesys connector.

Answer the call and obtain its ID

Use the Genesys conversationId associated with the agent's call. With a custom softphone, handle Genesys WebRTC session events and the corresponding Genesys conversation notifications. The SDK's media session ID is a separate identifier; correlate by conversationId.

The repository already contains a Genesys telephony provider that initializes genesys-cloud-webrtc-sdk, handles sessions, and uses the Genesys Platform API for call control. It belongs to the SCV connector app; it is not exported by either TrueServe embed SDK. The other team still needs Genesys user authentication, a configured WebRTC phone, queue access and call controls in its own app, or can use the existing Genesys agent desktop.

Genesys's WebRTC SDK softphone documentation describes its session/notification correlation and answer flow. That SDK is for authenticated agent telephony; do not substitute it for the anonymous customer-side test phone.

Resolve and authorize the TrueServe conversation

Keep the tenant tsf_* API token in the host backend. Use a token with agents:run for the embed flow. Register the host's exact origin as an active App client in Settings → Integrations → Embedding, with iframe/API access as needed.

After the host authorizes its user to access the selected Genesys call, its backend performs these requests:

Step Host and request Result
Resolve call conversation-api: GET /conversations/by-external-id/{genesysConversationId} with bearer token {conversation, surface_config_id, surface_config}
Bind embed ai-manager: POST /api/embed/mint-session with bearer token and body below {embedToken, expiresAt, surfaceUrl, iframeUrl}
Subscribe surface-app opens the authorized conversation stream Transcript and other events

The external-ID resolver returns conversation.id, session_id, agent_id, status and metadata. Use conversation.id for the embed. The Genesys connector normally uses the Genesys UUID as the TrueServe conversation ID, but resolving first verifies that the TrueServe record actually exists and belongs to the token's tenant.

An initial 404 can mean AudioHook has not created the record yet. Retry with a bounded wait, cancel it when the user switches calls, and show a useful timeout. Do not create a new chat conversation as a fallback. Check that the resolved base agent matches the expected transcription Copilot; the resolver selects the newest matching record, not an agent-filtered result.

{
  "agentId": "<published transcription Copilot base ID>",
  "conversationId": "<resolved conversation.id>",
  "audienceOrigin": "https://team-app.example",
  "ttlSec": 120
}

Use the conversation-api hostname for the first request and ai-manager for the second. Mint a fresh single-use embed token for each new iframe. Return only the embed response and authorized conversation ID to the browser.

Embed the transcript and consume events

Load /trueserve-embed-sdk.js from the matching surface-app deployment, then create a transcript view. The following host-owned backend endpoint performs the authorization, resolve and mint steps above:

<div id="live-transcript"></div>
<script src="https://surface-app.example/trueserve-embed-sdk.js"></script>
async function attachTranscript(genesysConversationId) {
  const response = await fetch("/api/trueserve/call-embed", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ genesysConversationId }),
  });
  if (!response.ok) throw new Error("Could not attach the call transcript");
  const { embedToken, surfaceUrl, conversationId } = await response.json();

  const embed = await TrueServeEmbed.create({
    container: "#live-transcript",
    surfaceUrl,
    token: embedToken,
    conversationId,
    loading: "eager",
    query: {
      viewMode: "transcript",
      hideInput: "true",
      transcriptSources: "agent_assist",
    },
  });

  embed.on("copilot-event", ({ conversationId: boundId, event }) => {
    if (boundId !== conversationId) return;
    if (!["interim_transcript", "final_transcript", "refined_transcript"]
      .includes(event.type)) return;
    // Forward into your app's transcript store; do not log call content.
    // event.data.text, role, message_key, revision, update_op, source
  });
  embed.on("error", ({ message }) => {
    // Display the connection error in your host UI.
  });
  return () => embed.destroy();
}

Retain and invoke the returned cleanup when switching calls or unmounting. copilot-event is the complete received event feed, including transcripts; its name does not require active Copilot workers. embed.on("event", ...) is the older conversation-event bridge.

Use the headless TypeScript SDK instead

Distribute the reviewed archive from shared/trueserve-sdk through the team's package mechanism. The checked-in package is @trueserve/sdk version 1.0.0; include the source revision/checksum in the handoff because the version alone does not identify the build.

For transcript-only use, route SSE through the host's authenticated backend:

import { DefaultApiResolver, TrueServeSDKClient } from "@trueserve/sdk";

class HostApiResolver extends DefaultApiResolver {
  sseStreamUrl(id: string): string {
    return `/api/trueserve/conversations/${encodeURIComponent(id)}/events`;
  }
}

const client = new TrueServeSDKClient({
  apiKey: "", // Legacy required option; this does not authenticate requests.
  apiResolver: new HostApiResolver(),
  transport: "sse",
});
client.on("error", (error) => { /* Display a connection error. */ });
const unsubscribe = client.subscribeToEvents((event) => {
  if (event.type === "final_transcript") {
    // Consume event.data using the transcript contract below.
  }
});
client.connect(resolvedConversationId);
// On unmount or call change:
// unsubscribe();
// client.disconnect();

The example BFF route is host-owned, not a shipped TrueServe endpoint. It must authorize that user's access to the conversation, proxy GET /sse/conversations/{id} with the server-held bearer token, stream without buffering, forward Last-Event-ID, and close its upstream request when the browser disconnects. Other SDK operations require corresponding resolver/BFF routes. The default resolver targets surface-app's BFF layout, not the public conversation-api layout.

Transcript event contract

Field Meaning
type interim_transcript, final_transcript, or optional refined_transcript
data.text Current transcript text
data.role user for the caller; agent for the human agent
data.source Copilot speech uses agent_assist
data.message_key, data.turn_id Stable utterance identity
data.revision, data.update_op, data.delta_text Revision and replace/append semantics
timestamp Milliseconds; see timing fields for finer-grained audio alignment
STATUS event Uses top-level status, including ended

Replace interim text with its final text instead of appending both. Deduplicate replayed events and handle revisions using the utterance identity, role and source. Use finalized text for downstream processing. The SDK's snapshot handles transcript merging when you want rendered state instead of raw events.

The inner transcript event's conversationId is a normalized numeric event identifier. It is not the UUID to use in API URLs. Retain the resolved UUID, or use the outer embed payload's conversationId.

External phone gaps and the handoff boundary

The phone quickstart is valid for direct TrueServe agent tests. It does not establish a Genesys customer call. In the current source:

  1. The dynamic session endpoint only issues transport: "webrtc".
  2. The stored SIP embed's SipRunner calls /api/auth/sip-credentials, which accepts an AI Manager user session with AIManager:RunAgents; an emulator embed cookie alone is rejected.
  3. SipRunner receives sip_integration_id but does not resolve its trunk. It assigns config.destination to both the dial target and X-Target-FQDN. The authenticated dialer keeps the DID and integration's byoc_trunk_fqdn separate.
  4. trueserve-call-connected currently has no conversation-ID payload. Obtain the Genesys ID from the agent softphone; the phone's local media ID is not a substitute.

These findings come from embed-mode.tsx, the SIP credentials route and the dynamic session route. Closing the external SIP path requires scoped embed-authorized SIP setup and server-side integration/target resolution, then an actual call test.

For a first integration, use a real phone or the authenticated TrueServe emulator as the caller and embed the transcript in the team's app. Supply the Copilot ID, Genesys number/queue and AudioHook mapping, deployment URLs, registered app origin, backend token arrangement, chosen SDK artifact and Genesys softphone setup. Keep the browser token exchange separate from the Genesys user's authentication.

Validate both caller and agent speech, final-event deduplication, call switching, hangup and reconnect in the target environment. Component tests do not prove Genesys routing, deployed revisions, browser media or organization setup.

See also