Subsessions

Subsessions (also known as breakout rooms) allow you to split users in a session into separate smaller sessions for group discussions or activities. The host can assign users to subsessions automatically (distributed evenly) or manually. A Video SDK session can have up to 50 subsessions.

Key concepts

  • Host/Manager privileges - Only the host or manager can create, open, manage, and close subsessions
  • Subsession states - NotStarted, InProgress, Closing, Closed
  • User states - Initial, Invited, InSubsession
  • Allocation patterns - Evenly distributed or manually assigned
  • Timer support - Optional countdown timer with auto-close functionality
  • User mobility - Users can move between subsessions or return to the main session

Init subsessions

After joining a session, call client.getSubsessionClient() to get the subsession client.

const subsession = client.getSubsessionClient();

Get subsession status

To check the current subsession status, call subsession.getSubsessionStatus(). This returns one of four possible states.

const status = subsession.getSubsessionStatus();
// Returns: SubsessionStatus.NotStarted | SubsessionStatus.InProgress | SubsessionStatus.Closing | SubsessionStatus.Closed

Get user status

To check the current user's subsession status, call subsession.getUserStatus(). This returns the user's current state in relation to subsessions.

const userStatus = subsession.getUserStatus();
// Returns: SubsessionUserStatus.Initial | SubsessionUserStatus.Invited | SubsessionUserStatus.InSubsession

Get current subsession

To get information about the subsession that the current user is in, call subsession.getCurrentSubsession().

const current = subsession.getCurrentSubsession();
// Returns: { subsessionId: string, subsessionName: string, userStatus: SubsessionUserStatus }

Configure subsessions

The host can configure subsessions.

Create subsessions

To create a subsession, call the subsession.createSubsessions() function. For the first parameter, pass in a number to specify how many subsessions to create, or an array of subsession names.

The second parameter specifies the allocation pattern.

  • SubsessionAllocationPattern.Evenly (value: 1) - Automatically distributes users evenly to each subsession.
  • SubsessionAllocationPattern.Manually (value: 2) - Allows you to assign users to subsessions manually (default).
// Create 3 subsessions with auto-assignment
subsession.createSubsessions(3, 1);
// Or create with custom names and manual assignment
subsession.createSubsessions(["Room A", "Room B", "Room C"], 2);

Manually assign users to a subsession

Get the subsession status using subsession.getSubsessionStatus(). Depending on the current status, there are two approaches to assigning users, subsessions not started, or subsessions already started.

  • Subsessions not started (SubsessionStatus.NotStarted or SubsessionStatus.Closed). Pass a subsessionList when calling subsession.openSubsessions() to pre-assign users to subsessions. Each item in the list can include not only the subsessionId and subsessionName, but also a userList. This assigns users to their respective subsessions when you open the subsessions.

    const subsessionList = subsession.getSubsessionList();
    function assignUserToSubsessionBeforeStarted(
        userId,
        subsessionId,
        subsessionList,
    ) {
        const session = subsessionList.find(
            (item) => item.subsessionId === subsessionId,
        );
        if (session) {
            const { userList } = session;
            if (!userList.find((user) => user.userId == userId)) {
                userList.push({ userId });
            }
        }
    }
    
  • Subsessions already started (SubsessionStatus.InProgress). In this case, use the subsession.assignUserToSubsession(userId,subsessionId) method to dynamically assign users to the active subsessions.

Open subsessions

After configuring your subsessions, call the subsession.openSubsessions() function to start the subsessions. The first parameter is the subsession list (obtained via getSubsessionList()), and the second parameter is an options object with the following properties:

  • isTimerEnabled - Enable a countdown timer for the subsessions
  • timerDuration - Duration in seconds (e.g., 1800 for 30 minutes)
  • isTimerAutoStopSubsession - Automatically close subsessions when timer expires
  • waitSeconds - Countdown in seconds before opening subsessions (gives users time to prepare)
  • isBackToMainSessionEnabled - Allow users to return to the main session
  • isAutoMoveBackToMainSession - Automatically move users back to the main session when subsessions close
  • isSubsessionSelectionEnabled - Allow users to choose which subsession to join
subsession.openSubsessions(subsession.getSubsessionList(), {
    isTimerEnabled: true,
    timerDuration: 1800, // 30 minutes
    isTimerAutoStopSubsession: false,
    waitSeconds: 60, // 60 second countdown before opening
    isBackToMainSessionEnabled: true,
    isAutoMoveBackToMainSession: true,
    isSubsessionSelectionEnabled: false,
});

See the corresponding event listeners.

Manage subsessions

The host can manage subsessions. Users can interact with sessions and the host.

Broadcast to subsessions

The host can broadcast both text messages and voice to all subsessions.

Broadcast text message

subsession.broadcast("Hello World");

See the subsession-broadcast-message event for how users receive broadcast messages.

Broadcast voice

The host can broadcast their audio to all subsessions simultaneously. Users must have joined audio to use this feature.

// Start broadcasting voice
await subsession.startBroadcastVoice();
// Stop broadcasting voice
await subsession.stopBroadcastVoice();

See the subsession-broadcast-voice event for how the SDK notifies users when voice broadcast starts or stops.

Chat in subsession

Users can chat in a subsession. The SDK scopes the chat to the subsession the user is in.

Ask for help

To have a user notify the host in a subsession that they need attention, call the subsession.askForHelp() function. This is typically used by attendees who need assistance.

subsession.askForHelp();

See the corresponding event listeners for how the host receives and responds to help requests.

Postpone response to help

When a user asks for help, the host can respond by either joining their subsession or postponing. To postpone, call the subsession.postponeHelping() function with the user's ID. This notifies the user that the host will assist them later.

subsession.postponeHelping(userId);

The user will receive a response via the subsession-ask-for-help-response event.

List users in a subsession

To list subsessions including users in the subsession, call the subsession.getSubsessionList() function. If the host calls this function they will get all the subsessions. If a user calls the function, they will get their current subsession.

subsession.getSubsessionList();

List users not in a subsession

To list users who are not in a subsession, call the subsession.getUnassignedUserList() function.

subsession.getUnassignedUserList();

Get subsession options

To get the current subsession configuration options, call the subsession.getSubsessionOptions() function. This returns the options set when opening subsessions.

const options = subsession.getSubsessionOptions();

Navigate subsessions

Users can navigate to subsessions, and the host can move users between them.

Join subsession

To join a subsession, call the subsession.joinSubsession() function. This function is useful to place a user who joins the session after you open the subsessions.

subsession.joinSubsession(subsessionId);

Leave subsession

To leave a subsession, call the subsession.leaveSubsession() function.

subsession.leaveSubsession();

Move user to subsession

To have the host move a user to a subsession, call the subsession.moveUserToSubsession() function.

subsession.moveUserToSubsession(userId, subsessionId);

Move user back to main session

To have the host move a user back to the main session, call the subsession.moveBackToMainSession() function.

subsession.moveBackToMainSession(userId);

Close subsessions

To close all subsessions, call the subsession.closeAllSubsessions() function.

subsession.closeAllSubsessions();

Event listeners

Event listeners are essential for tracking subsession state changes and user interactions. The following are all available subsession events.

Subsession state change

The subsession-state-change event fires whenever the subsession status changes. This is the primary event for tracking subsession lifecycle.

client.on("subsession-state-change", (payload) => {
    console.log(payload.status);
    // status values:
    // - SubsessionStatus.NotStarted (0)
    // - SubsessionStatus.InProgress (1)
    // - SubsessionStatus.Closing (2)
    // - SubsessionStatus.Closed (3)
});

Subsession state change payload

{
    status: SubsessionStatus;
}

Subsession countdown

The subsession-countdown event fires when you open subsessions with a timer. Use this to display a countdown timer to users.

client.on("subsession-countdown", (payload) => {
    console.log(`Time remaining: ${payload.countdown} seconds`);
});

Subsession countdown payload

{
    countdown: number; // Remaining seconds
}

Closing subsession countdown

The closing-subsession-countdown event fires when the host closes subsessions with a countdown. This gives users time to wrap up their discussions.

client.on("closing-subsession-countdown", (payload) => {
    console.log(`Subsessions closing in ${payload.countdown} seconds`);
});

Closing subsession countdown payload

{
    countdown: number; // Seconds until subsessions close
}

Subsession time up

The subsession-time-up event fires when the subsession timer reaches zero. Hosts typically use this to decide whether to close subsessions or extend the time.

client.on("subsession-time-up", () => {
    console.log("Subsession timer has expired");
});

Main session user updated

The main-session-user-updated event fires when unassigned users in the main session change. This helps track changes to the unassigned user list.

client.on("main-session-user-updated", () => {
    // Refresh unassigned user list
    const unassignedUsers = subsession.getUnassignedUserList();
    console.log("Main session users updated", unassignedUsers);
});

Invite to join subsession

The subsession-invite-to-join event fires when the host invites a user to join a specific subsession. Users receive this event and can choose to accept or decline.

client.on("subsession-invite-to-join", (payload) => {
    console.log(
        `You've been invited to join ${payload.subsessionName} (ID: ${payload.subsessionId})`,
    );
    // User can call subsession.joinSubsession(payload.subsessionId) to accept
});

Invite to join subsession payload

{
    subsessionId: string;
    subsessionName: string;
}

Invite back to main session

The subsession-invite-back-to-main-session event fires when the host invites a user to return to the main session from a subsession.

client.on("subsession-invite-back-to-main-session", (payload) => {
    console.log(`${payload.inviterName} invited you back to the main session`);
    // User can call subsession.leaveSubsession() to return
});

Invite back to main session payload

{
    inviterId: number;
    inviterGuid: string;
    inviterName: string;
}

Broadcast message

The subsession-broadcast-message event fires when the host broadcasts a text message to all subsessions.

client.on("subsession-broadcast-message", (payload) => {
    console.log(`Broadcast message: ${payload.message}`);
    console.log(`From: ${payload.senderName}`);
});

Broadcast message payload

{
    message: string;
    senderName: string;
}

Broadcast voice

The subsession-broadcast-voice event fires when the host starts or stops broadcasting their voice to all subsessions.

client.on("subsession-broadcast-voice", (payload) => {
    if (payload.status) {
        console.log("Host is broadcasting voice to all subsessions");
    } else {
        console.log("Host stopped broadcasting voice");
    }
});

Broadcast voice payload

{
    status: boolean; // true = started, false = stopped
}

Ask for help

The subsession-ask-for-help event fires when a user in a subsession requests help from the host. Only the host receives this event.

client.on("subsession-ask-for-help", (payload) => {
    console.log(
        `${payload.displayName} in ${payload.subsessionName} is asking for help`,
    );
    // Host can join the subsession or postpone
    // subsession.joinSubsession(payload.subsessionId)
    // subsession.postponeHelping(payload.userId)
});

Ask for help payload

{
    userId: number;
    displayName: string;
    subsessionId: string;
    subsessionName: string;
}

Ask for help response

The subsession-ask-for-help-response event fires when the host responds to a help request. The user who requested help receives this event.

client.on("subsession-ask-for-help-response", (payload) => {
    switch (payload.result) {
        case 0: // AskHostHelpResponse.Received
            console.log("The host has received your request");
            break;
        case 1: // AskHostHelpResponse.Busy
            console.log("The host is currently busy in another subsession");
            break;
        case 2: // AskHostHelpResponse.Ignore
            console.log("The host will assist you later");
            break;
        case 3: // AskHostHelpResponse.AlreadyInRoom
            console.log("The host is already in your subsession");
            break;
    }
});

Ask for help response payload

{
    result: AskHostHelpResponse; // 0=Received, 1=Busy, 2=Ignore, 3=AlreadyInRoom
}

Event listener usage example

Here's a complete example showing how to handle subsession events in your application.

const zmClient = ZoomVideo.createClient();
const subsessionClient = zmClient.getSubsessionClient();
// Track subsession state
client.on("subsession-state-change", (payload) => {
    updateSubsessionUI(payload.status);
});
// Handle invitations
client.on("subsession-invite-to-join", (payload) => {
    showInviteDialog(payload.subsessionName, () => {
        subsessionClient.joinSubsession(payload.subsessionId);
    });
});
// Display countdown
client.on("subsession-countdown", (payload) => {
    updateCountdownDisplay(payload.countdown);
});
// Host: handle help requests
if (zmClient.isHost()) {
    client.on("subsession-ask-for-help", (payload) => {
        showHelpRequest(payload.displayName, payload.subsessionName, () => {
            subsessionClient.joinSubsession(payload.subsessionId);
        });
    });
}
// User: handle help response
client.on("subsession-ask-for-help-response", (payload) => {
    showHelpResponseMessage(payload.result);
});
// Display broadcasts
client.on("subsession-broadcast-message", (payload) => {
    showBroadcastNotification(payload.senderName, payload.message);
});

Best practices

Here are some best practices.

Listen to state changes

Always listen to subsession-state-change to keep your UI synchronized with the actual subsession state. This event fires whenever subsessions open, close, or change status.

client.on("subsession-state-change", (payload) => {
    // Update UI based on payload.status
    const subsessionList = subsessionClient.getSubsessionList();
    const currentSubsession = subsessionClient.getCurrentSubsession();
    updateUI(payload.status, subsessionList, currentSubsession);
});

Handle user assignment changes

When users join or leave the session while subsessions are active, listen to main-session-user-updated to refresh the unassigned user list.

client.on("main-session-user-updated", () => {
    const unassignedUsers = subsessionClient.getUnassignedUserList();
    updateUnassignedUsersList(unassignedUsers);
});

Provide clear user feedback

When implementing invite-to-join functionality, give users clear options to accept or decline.

client.on("subsession-invite-to-join", (payload) => {
    showModal({
        title: "Join Subsession",
        message: `You've been assigned to ${payload.subsessionName}`,
        onAccept: () => subsessionClient.joinSubsession(payload.subsessionId),
        onDecline: () => console.log("User declined invitation"),
    });
});

Implement help request workflow

For hosts, implement a clear workflow for handling help requests.

client.on("subsession-ask-for-help", (payload) => {
    showHelpDialog({
        message: `${payload.displayName} in ${payload.subsessionName} needs help`,
        onJoin: () => subsessionClient.joinSubsession(payload.subsessionId),
        onLater: () => subsessionClient.postponeHelping(payload.userId),
    });
});

Handle timer expiration

When using timers, decide whether to automatically close subsessions or prompt the host.

client.on("subsession-time-up", () => {
    if (zmClient.isHost()) {
        showConfirmDialog({
            message: "Subsession timer expired. Close all subsessions?",
            onConfirm: () => subsessionClient.closeAllSubsessions(),
            onCancel: () => console.log("Keep subsessions open"),
        });
    }
});

Common scenarios

Here are some examples of common scenarios.

Scenario 1: Create and open subsessions with even distribution

const subsessionClient = client.getSubsessionClient();
// Create 5 subsessions with even distribution
await subsessionClient.createSubsessions(5, 1);
// Open with 30-minute timer
subsessionClient.openSubsessions(subsessionClient.getSubsessionList(), {
    isTimerEnabled: true,
    timerDuration: 1800,
    isAutoMoveBackToMainSession: true,
});

Scenario 2: Manual assignment before opening

// Create subsessions manually
await subsessionClient.createSubsessions(3, 2);
// Get subsessions and users
const subsessions = subsessionClient.getSubsessionList();
const unassignedUsers = subsessionClient.getUnassignedUserList();
// Assign users to subsessions before opening
function assignUserToSubsessionBeforeStarted(
    userId,
    subsessionId,
    subsessionList,
) {
    const subsession = subsessionList.find(
        (item) => item.subsessionId === subsessionId,
    );
    if (subsession) {
        const { userList } = subsession;
        if (!userList.find((user) => user.userId == userId)) {
            userList.push({ userId });
        }
    }
}
// Assign each user
unassignedUsers.forEach((user, index) => {
    const targetSubsessionId =
        subsessions[index % subsessions.length].subsessionId;
    assignUserToSubsessionBeforeStarted(
        user.userId,
        targetSubsessionId,
        subsessions,
    );
});
// Open subsessions
subsessionClient.openSubsessions(subsessions, {
    isTimerEnabled: false,
    isBackToMainSessionEnabled: true,
});

Scenario 3: Move users between subsessions dynamically

// This only works when subsessions are in progress
if (subsessionClient.getSubsessionStatus() === SubsessionStatus.InProgress) {
    subsessionClient.moveUserToSubsession(userId, targetSubsessionId);
}

Scenario 4: Close subsessions with countdown

// Close all subsessions (triggers closing countdown if configured)
subsessionClient.closeAllSubsessions();
// Listen for closing countdown
client.on("closing-subsession-countdown", (payload) => {
    showNotification(`Returning to main session in ${payload.countdown}s`);
});

More subsession features

For the full set of subsession features, see SubsessionClient in the Video SDK Reference.