Embed the demo phone¶
Status: Current | Last reviewed: 2026-10-07
Use <trueserve-phone> when an external web app needs to select a published
agent, collect test-call options, and render the iPhone-style emulator. The
element owns a responsive AI Manager iframe. It removes page scrollbars and the
phone drop shadow, grants microphone access, validates iframe messages, and
exposes call controls as DOM methods.
This dynamic API is direct TrueServe WebRTC only. It does not accept a Genesys integration or DID and does not ring a Genesys agent softphone. Stored SIP embed configurations exist, but their current runtime still requires an AI Manager login and has a trunk-target resolution gap. See Live Genesys transcripts before handing this phone to an external team for Genesys calls.
The session direction selects the initial phone experience:
outboundopens on Home with the selected agent in the banner at the top. The customer can click the green phone icon or anywhere on Home to start the call.inboundopens the selected agent as an iPhone-style Contact card. The customer starts the test with the green call action on that card.
autoConnect: true remains available for outbound automation and starts the call without waiting for the Home-screen click.
Hosted resources¶
| Resource | Vanguard URL |
|---|---|
| Phone element | https://ai-manager.vanguard.dev.trueserve.deloitte.com/embed/trueserve-phone.js |
| OpenAPI document | https://ai-manager.vanguard.dev.trueserve.deloitte.com/openapi.json |
| Rendered API reference | https://developer.vanguard.dev.trueserve.deloitte.com/api/ai-manager-api/ |
Use the matching environment hostname outside Vanguard.
Backend endpoint¶
Create a tenant API token with agents:read and emulator:run. Keep that
token in your backend. Your browser endpoint accepts the selected agent and
supported call options, calls AI Manager, and returns iframeUrl.
This Next.js route shows the contract:
// app/api/trueserve/phone-session/route.ts
export async function POST(request: Request) {
const input = await request.json();
const response = await fetch(
`${process.env.AI_MANAGER_URL}/api/v1/emulator/sessions`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TRUESERVE_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
agentId: input.agentId,
audienceOrigin: process.env.APP_PUBLIC_ORIGIN,
direction: input.direction,
autoConnect: input.autoConnect,
callerName: input.callerName,
callerNumber: input.callerNumber,
sessionVariables: input.sessionVariables,
sttLanguage: input.sttLanguage,
}),
cache: "no-store",
},
);
const body = await response.json();
if (!response.ok) {
return Response.json(
{ error: body.error ?? "Could not create phone session" },
{ status: response.status },
);
}
return Response.json(
{
iframeUrl: body.iframeUrl,
expiresAt: body.expiresAt,
sessionExpiresAt: body.sessionExpiresAt,
emulator: body.emulator,
},
{ headers: { "Cache-Control": "no-store" } },
);
}
Set APP_PUBLIC_ORIGIN to the exact scheme, host, and port of the page that
contains the phone. AI Manager binds the session to that origin.
Browser integration¶
Load the hosted element and add one tag:
<script
src="https://ai-manager.vanguard.dev.trueserve.deloitte.com/embed/trueserve-phone.js"
></script>
<trueserve-phone
id="demo-phone"
session-url="/api/trueserve/phone-session"
autoload="false"
></trueserve-phone>
Pass the selected agent and customizations as a JavaScript property, then ask the element to mint a fresh session:
await customElements.whenDefined("trueserve-phone");
const phone = document.querySelector("#demo-phone");
async function openPhone(selection) {
phone.sessionRequest = {
agentId: selection.pipelineId,
direction: "outbound",
autoConnect: false,
callerName: selection.customerName,
sessionVariables: selection.variables,
sttLanguage: selection.language,
};
await phone.load();
}
Call load() after each agent or customization change. AI Manager gives each
iframe a one-time bootstrap URL, so a page reload or remount needs a new
session.
Static markup can use autoload and a JSON attribute:
<trueserve-phone
session-url="/api/trueserve/phone-session"
session-request='{"agentId":"3026","callerName":"Demo Customer"}'
></trueserve-phone>
Sizing and presentation¶
The element uses a 436 / 902 aspect ratio and scales the phone to its host
box. Set its width from your app CSS:
.phone-panel trueserve-phone {
--ts-phone-width: min(436px, 100%);
}
The default fit="contain" and elevation="none" settings prevent iframe
scrollbars and remove the external phone shadow. Set elevation="default"
when a standalone page needs the original drop shadow. Presentation changes
take effect the next time you call load().
Multimodal SMS flow¶
The embedded phone subscribes to the active conversation after the call connects. When the agent sends a demo multimodal SMS, the phone shows the iOS notification banner. The customer taps the banner to open Messages, then taps the link to open Safari. Safari renders trusted A2UI components directly in the phone's React tree, and component actions return to the same conversation.
Inside the AI Manager tester this demo SMS is not optional or configurable: any
agent with multimodal capability enabled always delivers the link to the
simulated phone (the tester sends X-Multimodal-Sms-Delivery: demo_notification
for every multimodal call, whether or not the agent has a production SMS
integration). Only real calls placed through a live integration use the
carrier-SMS path. The standalone embedded web component instead sets
X-Suppress-Multimodal-Sms so it renders A2UI inline without a demo banner.
The host page contains one iframe: the AI Manager phone boundary used for session isolation, microphone permissions, and media. Messages, Safari, and A2UI do not create another iframe. Non-TrueServe URLs are delegated to the host browser instead of being framed inside the phone.
Calls and events¶
The element exposes these methods:
phone.startCall();
phone.endCall();
phone.setMuted(true);
phone.triggerIncomingCall({ caller_name: "Alex Rivera", ani: "+15555550100" });
Listen for element events instead of handling postMessage:
phone.addEventListener("trueserve-ready", () => console.log("ready"));
phone.addEventListener("trueserve-call-connected", () => console.log("connected"));
phone.addEventListener("trueserve-call-ended", (event) => {
console.log("seconds", event.detail.duration);
});
phone.addEventListener("trueserve-error", (event) => {
console.error(event.detail.message);
});
The full event set is trueserve-ready, trueserve-call-started,
trueserve-call-connected, trueserve-call-ended,
trueserve-incoming-call, trueserve-error, trueserve-session-created, and
trueserve-session-error.
trueserve-call-connected currently carries no conversation ID. For a Genesys
call, obtain conversationId from the Genesys agent session and resolve the
TrueServe record as described in the Genesys guide.
Browser policy checklist¶
- Add the AI Manager origin to
script-srcandframe-srcin your Content Security Policy. - Preserve microphone delegation if your app sends a
Permissions-Policyheader. The component sets the iframeallow="microphone; autoplay"attribute. - Start calls from a click or tap when the browser requires a user gesture for microphone permission or audio playback.
- Send
iframeUrlto the browser. Keep the tenanttsf_*token in your backend environment.
Direct iframe fallback¶
Apps that cannot load a custom element can assign iframeUrl to a plain iframe:
<iframe
src="https://ai-manager.example/embed/phone?t=ONE_TIME_TOKEN"
title="TrueServe phone"
allow="microphone; autoplay"
scrolling="no"
style="width:436px;aspect-ratio:436/902;border:0;overflow:hidden;background:transparent"
></iframe>
The custom element remains the preferred web integration because it handles session loading, sizing, origin checks, call controls, and events.