Skip to content

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:

  • outbound opens 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.
  • inbound opens 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-src and frame-src in your Content Security Policy.
  • Preserve microphone delegation if your app sends a Permissions-Policy header. The component sets the iframe allow="microphone; autoplay" attribute.
  • Start calls from a click or tap when the browser requires a user gesture for microphone permission or audio playback.
  • Send iframeUrl to the browser. Keep the tenant tsf_* 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.

See also