On this page

Implement Live Digital Human Broadcasting

2026-08-14

This document describes how to quickly integrate the client SDK (ZEGO Express SDK) to implement live digital human broadcasting.
Unlike digital human video calls, digital human broadcasting is a one-way viewing scenario - users do not interact with the digital human, they simply play stream the digital human's broadcast content for viewing.
The client only needs to log in to an RTC room and play stream; it does not need to capture or publish stream the user's audio and video. After creating a broadcast instance, the server can proactively make the digital human broadcast specified text via the TTS API.
Suitable for scenarios such as digital human live streaming, news broadcasting, event hosting, and scripted broadcasts.

Differences from Digital Human Video Calls

ItemDigital Human Video CallLive Digital Human Broadcasting
Interaction modeTwo-way: the digital human responds after the user speaksOne-way: users only view the digital human's broadcast content
Use casesConversational AI, AI customer service, digital human tutorDigital human live streaming, news broadcasting
Does the client need to capture user audio?YesNo
Digital human driving mechanismAfter receiving user audio, LLM outputs a response and drives the digital human via TTSBroadcast content is fully controlled by the server, driving the digital human via TTS
Instance creation APICreateDigitalHumanAgentInstanceCreateLiveDigitalHumanAgentInstance
Requires user_id, user_stream_idYesNo

Prerequisites

  • You have created a project in the ZEGOCLOUD Console and obtained a valid AppID and ServerSecret (for server API signing and RTC Token04 generation). For details, see Console - Project Information.
  • You have contacted ZEGOCLOUD Technical Support to enable the Digital Human PaaS service and the relevant API permissions.
  • You have obtained a valid digital_human_id (for testing, you can use the public ID: 63c3aa64-1d80-4b04-a0be-1c65614eb7eb).
  • You have integrated the broadcast digital human server APIs as described in Server Quick Start Guide.
  • You have contacted ZEGOCLOUD Technical Support to obtain the ZEGO Express SDK optimized for AI Agent and integrated it into your project.
Note

Digital human broadcasting does not require microphone permissions and does not publish local streams. If the same app also includes voice call or digital human video call entries, those entries still need to request microphone permissions as described in their respective documentation.

Sample Code

The following is the business backend sample code for integrating the Conversational AI Agent API. You can refer to the sample code to implement your own business logic.

The following is the client sample code. You can refer to the sample code to implement your own business logic.

The following video demonstrates how to run through the server and client (Web) sample code and interact with the agent via voice.

Overall Business Flow

  1. On the server side, refer to the Server Quick Start Guide to run through the business backend sample code and deploy the business backend.
    • Integrate the Conversational AI Agent API to manage agents.
  1. On the client side, run through the sample code.
    • Create and manage agents through the business backend.
    • Integrate ZEGO Express SDK for real-time communication.

After completing the above two steps, you can view digital human broadcasts.

Core Feature Implementation

Integrating ZEGO Express SDK

The Web quickstart uses zego-express-engine-webrtc. The broadcast entry does not create audio streams or call startPublishingStream, so no microphone permission is needed:

npm install zego-express-engine-webrtc
import { ZegoExpressEngine } from "zego-express-engine-webrtc";

// appID: number, obtained from ZEGOCLOUD Console project information
// server: signaling server address; for ZEGO Express SDK 3.7.0 and above, you can use the Server address from the console or leave it as an empty string
const zg = new ZegoExpressEngine(appID, "");

Below are commonly used variable declarations/configurations for the Web client:

// Obtain from ZEGOCLOUD Console
const APP_ID = 1234567890; // AppID, number type
const SERVER = ""; // Signaling server address, can be empty string for 3.7.0+
// Obtain from business backend
const BASE_URL = "http://your-server-host:3000"; // Business backend address
const ROOM_ID = "room_xxx"; // RTC room ID
const USER_ID = "user_xxx"; // User ID
const DIGITAL_HUMAN_ID = "63c3aa64-1d80-4b04-a0be-1c65614eb7eb"; // Digital human ID
const CONFIG_ID = "web"; // Use "web" for Web client


// Module-level variables, used by roomStreamUpdate / remoteCameraStatusUpdate / exit
let agentStreamId = "";
let agentInstanceId = "";
const loginResult = await zg.loginRoom(roomID, token, {
    userID,
    userName,
});

if (!loginResult) {
  throw new Error("Failed to log in to RTC room");
}

The current quickstart's common initialization logic still calls checkSystemRequirements to check WebRTC and microphone capabilities for compatibility with the voice call entry. If your Web app only implements digital human broadcasting, you can remove the microphone check.

Notify the Business Backend to Create a Broadcast Digital Human Instance

The client calls the business backend's POST /api/start-live-digital-human. Upon receiving the request, the business backend calls ZEGOCLOUD's CreateLiveDigitalHumanAgentInstance API to create a broadcast digital human instance. For detailed parameters, see Create Broadcast Digital Human Instance. In RTC mode, the request body must at least contain room_id, digital_human_id, and config_id; user_id and user_stream_id are not required.

{
  "room_id": "room_xxxxxxxx",
  "digital_human_id": "digital_human_xxxxxxxx",
  "config_id": "mobile"
}

digital_human_id corresponds to the server-side DigitalHuman structure. Its core fields are as follows:

FieldDescription
digital_human_idDigital human ID, used to specify the digital human appearance.
encode_codeEncoding type. Only supports mobile (Android/iOS) and web (Web). Android and iOS use mobile, Web uses web.

For Android and iOS, config_id uses mobile; for Web, it uses web. The valid values for DigitalHumanConfig.EncodeCode are only mobile and web.

When calling the ZEGOCLOUD API, the business backend needs to construct the digital_human_id and config_id into a DigitalHuman object:

{
    "DigitalHumanId": "63c3aa64-1d80-4b04-a0be-1c65614eb7eb",
    "ConfigId": "mobile",
    "EncodeCode": "H264"
}

The successful server response should return the following information to the client:

FieldPurpose
agent_instance_idFor calling the proactive TTS and stop instance APIs
agent_stream_idThe ID of the digital human stream in the RTC room
agent_user_idThe digital human's user ID in the RTC room
digital_human_configConfiguration for initializing the Android/iOS Digital Human SDK

Web uses fetch to directly request the business backend. The example only passes the RTC room ID, so no local user or local stream information is sent:

async function startLiveDigitalHuman(roomId: string) {
  const response = await fetch(`${baseURL}/api/start-live-digital-human`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
    digital_human_id: config.digitalHuman.id,
    config_id: config.digitalHuman.configId,
    room_id: roomId,
    }),
  });
  const result = await response.json();
  if (result.code !== 0) throw new Error(result.message);
  return result;
}

User Enters the Room (No Stream Publishing)

Broadcast digital human only requires logging in to the RTC room and receiving the digital human stream. The client does not need to create a local stream or call startPublishingStream.

Web logs in to the room directly. The broadcast scenario does not create audio streams or publish local streams:

// 1. Obtain Token from business backend
const tokenRes = await fetch(`${BASE_URL}/api/zego-token?userId=${USER_ID}&roomId=${ROOM_ID}`);
const { token } = await tokenRes.json();

// 2. Log in to RTC room
await zg.loginRoom(ROOM_ID, token, { userID: USER_ID, userName: USER_ID });

// 3. Create broadcast digital human instance
const result = await startLiveDigitalHuman(ROOM_ID);
// 4. Extract and save agent_stream_id and agent_instance_id for subsequent play stream, TTS, and exit
agentStreamId = result.agent_stream_id;
agentInstanceId = result.agent_instance_id;

Initializing the Digital Human SDK and Custom Rendering

Android and iOS need to pass the raw video frames and SEI data received by the ZEGO Express SDK to the Digital Human SDK, which then renders the digital human. You must enable custom video rendering before calling startPlayingStream.

The Web client does not integrate the Digital Human SDK and does not require custom rendering configuration. It directly uses the ZEGO Express SDK to play the digital human video stream.

Playing the Digital Human Stream

After creating the instance, the client uses the agent_stream_id returned by the server to play the digital human stream. The timing for playing streams varies slightly across platform quickstarts: Android plays the stream immediately after the create instance API succeeds; iOS and Web match the target stream via room stream update callbacks before playing.

Listen for roomStreamUpdate and create a remote stream view after matching the agentStreamId returned by the server. The agentStreamId comes from the agent_stream_id returned by the create instance API, which was extracted from the response after logging in to the room:

let remoteView: any = null;

zg.on(
  "roomStreamUpdate",
  async (
    roomID: string,
    updateType: "DELETE" | "ADD",
    streamList: ZegoStreamList[],
  ) => {
    if (updateType === "ADD" && streamList.length > 0) {
      for (const stream of streamList) {
        // Only play the digital human stream, filter out other streams in the room
        if (stream.streamID !== agentStreamId) continue;
        const mediaStream = await zg.startPlayingStream(stream.streamID);
        remoteView = await zg.createRemoteStreamView(mediaStream);
        remoteView?.playAudio();
        break;
      }
    }
  },
);

// The digital human video stream is published by the server. Camera status changing to OPEN indicates the video stream is ready, at which point video playback begins
zg.on(
  "remoteCameraStatusUpdate",
  (streamID: string, status: "OPEN" | "MUTE") => {
    if (streamID === agentStreamId && status === "OPEN") {
      remoteView?.playVideo("remoteStreamView");
    }
  }
);
CDN Mode Stream Playback

The above describes the RTC mode stream playback method. If using CDN mode, the client does not need to log in to an RTC room or follow the above flow to play an RTC stream. Instead, it can directly use a generic player (such as an HLS/FLV player) to play the cdn_url returned by the business backend to view the digital human broadcast. CDN mode does not require integrating the Digital Human SDK.

Proactively Making the Digital Human Broadcast Text

After the digital human instance is successfully created, the client calls the business backend's POST /api/send-agent-instance-tts, passing in the instance ID and text:

{
  "agent_instance_id": "2075416950128779264",
  "text": "Hello developer, welcome to ZEGOCLOUD AI Agent."
}

The maximum length of text is 300 characters. You can also pass additional parameters such as add_history, priority, and same_priority_option as needed. For details, see Proactive TTS Invocation.

Instance Lifecycle

A broadcast digital human instance is automatically destroyed after being idle (no broadcast tasks) for more than 900 seconds. The business side can adjust this idle timeout via MaxIdleTime. If you need to keep the instance alive for a long time, periodically invoke proactive TTS or increase MaxIdleTime.

const response = await fetch(`${baseURL}/api/send-agent-instance-tts`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    agent_instance_id: agentInstanceId,
    text,
  }),
});
const result = await response.json();
if (result.code !== 0) throw new Error(result.message);

The Web quickstart displays a TTS input box in broadcast mode and saves the agent_instance_id returned by the server to the page state.

Leaving the Room and Ending the Broadcast

When exiting, you need to stop the Agent instance, stop playing the stream, log out of the RTC room, and destroy the client SDK. Regardless of whether the stop API call succeeds, RTC resources should be released to avoid lingering rooms and engines.

async function logoutRoom() {
  await fetch(`${baseURL}/api/stop`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ agent_instance_id: agentInstanceId }),
  });
  zg.stopPlayingStream(agentStreamId);
  zg.logoutRoom(roomID);
  agentInstanceId = "";
}

The broadcast scenario has no local audio stream, so there is no need to destroy audio capture streams.

RTC and CDN Modes

This document uses RTC mode as an example: the client logs in to a room and plays the agent_stream_id. If you need large-scale live streaming, you can use CDN mode:

ModeInstance Creation ParametersClient Playback MethodUse Cases
RTCroom_idZEGO Express SDK to play RTC streamLow latency, small-scale interaction
CDNcdn_urlUse a player to play CDN streamLarge-scale live streaming

Both RTC and CDN modes use agent_instance_id to call the proactive TTS and stop instance APIs. CDN mode does not require the client to log in to an RTC room or integrate the Digital Human SDK. For CDN stream playback, you can use any CDN-compatible playback method without special configuration.

Digital humans support both RTC and CDN stream playback. If you need a large number of users to watch digital human broadcasts, such as in e-commerce live streaming or live classroom scenarios, you can use CDN live streaming mode. In CDN mode, the Web client does not need to integrate the ZEGO Express SDK. Simply use any Web player that supports HLS/FLV (such as hls.js or video.js) to play the cdn_url.

Listening to Callbacks

Please listen to the ZEGO Express SDK's room login, play stream status, and error callbacks. Record the agent_instance_id, agent_stream_id, request_id, and error information in the business backend to help troubleshoot instance creation, stream playback, or TTS failures. The request_id is returned by the ZEGOCLOUD server and is a key identifier for troubleshooting server-side issues. When an exception occurs, be sure to provide this information along with the above details to ZEGOCLOUD Technical Support.

During integration testing, it is strongly recommended to listen for callbacks received by the business backend (events where the Event is Exception). Use Data.Code and Data.Message to quickly identify issues with LLM/TTS parameter configurations. For callback and error code details, see Receiving Callbacks and Exception Event Error Codes.

Note

If the digital human display stays on a static image, please check the following: whether the digital human configuration is valid, whether agent_stream_id is correct, whether custom video rendering was enabled before startPlayingStream, and whether video frames and SEI data are being passed to the Digital Human SDK.

Troubleshooting Checklist

If you encounter issues during integration, check against the following checklist item by item.

Web Troubleshooting Checklist

SymptomTroubleshooting Direction
Room login failedCheck if the Token is valid, if AppID matches; for version 3.7.0 and above, server can be an empty string.
Instance creation failedConfirm that digital_human_id, config_id (Web uses web), and room_id are correct, and that the business backend signature is valid.
No video / No audioFilter by agentStreamId in roomStreamUpdate before calling startPlayingStream; wait for remoteCameraStatusUpdate to be OPEN before calling playVideo.
Video container not displayingConfirm the container ID passed to playVideo is remoteStreamView (check spelling).
TTS not broadcastingConfirm agent_instance_id is correct, text ≤ 300 characters, and the instance has not been destroyed due to 900 seconds of idle time.

For more error codes, see Exception Event Error Codes. For callback listening, see Receiving Callbacks.

2026-07-28

Previous

Quick Start with Digital Human

Next

Display Subtitles