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:
- The dynamic session endpoint only issues
transport: "webrtc". - The stored SIP embed's
SipRunnercalls/api/auth/sip-credentials, which accepts an AI Manager user session withAIManager:RunAgents; an emulator embed cookie alone is rejected. SipRunnerreceivessip_integration_idbut does not resolve its trunk. It assignsconfig.destinationto both the dial target andX-Target-FQDN. The authenticated dialer keeps the DID and integration'sbyoc_trunk_fqdnseparate.trueserve-call-connectedcurrently 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.