Session Management

Moderators have a rich set of controls to manage the lifecycle and behaviour of a live video session — from recording and muting to dropping users, locking the room, and concluding the conference.

Recording

Legacy Recording

Legacy recording captures each participant's stream individually as MKV files. Post-session, EnableX can transcode these into a single composite MP4 via a subscription-based service. Recording can be started and stopped multiple times in a session with no limit on the number of segments.

Auto-recording

Configure "auto_recording": true in room settings to start recording automatically when the session begins. The moderator can still stop and restart recording manually.

Start Recording

room.startRecord(function(result, error) {
  if (result === 0) {
    console.log("Recording started");
  }
});

room.addEventListener("room-record-on", function(event) {
  showRecordingIndicator();
  // event.message.moderatorId — who started recording
});

Stop Recording

room.stopRecord(function(result, error) {
  if (result === 0) {
    console.log("Recording stopped");
  }
});

room.addEventListener("room-record-off", function(event) {
  hideRecordingIndicator();
});

Live Recording

Live recording (SDK v2.1.2+) creates a transcoded MP4 file in real time during the session using a custom layout. Unlike legacy recording, the output file is available within minutes of the session ending — no long transcoding queue. You link a custom web view that acts as the recording layout.

Start Live Recording

var recordConfig = {
  urlDetails: {
    url:          "https://your-custom-layout.example.com/",
    chatOverlay:  false,
    layOut:       {}   // Any custom layout options your view handles
  }
};

room.startLiveRecording(recordConfig, function(resp) {
  if (resp.data.status.resultCode === 0) {
    console.log("Live recording initiated");
  }
});

room.addEventListener("room-live-recording-on", function(event) {
  showLiveRecordingBadge();
  // event.data.startedBy — who started it
});

room.addEventListener("room-live-recording-failed", function(event) {
  showError("Live recording failed to start");
});

Stop Live Recording

room.stopLiveRecording({}, function(resp) {
  if (resp.data.status.resultCode === 0) {
    console.log("Live recording stopped");
  }
});

room.addEventListener("room-live-recording-off", function(event) {
  hideLiveRecordingBadge();
});

Watermarking Recorded Files

Watermark a PNG image (max 140×140 px) onto your transcoded recordings. Set it up in Portal: Video → Settings → Recording → Watermarking. Then create the room with "watermark": true:

{
  "name": "My Meeting",
  "settings": {
    "watermark": true
  }
}
Watermarks apply to transcoded MP4 files only

Individual MKV stream recordings are not watermarked. Live-recording files produced by startLiveRecording() are also not affected by this setting.

Getting Recording Files

EnableX produces three types of output files:

File TypeFormatAvailability
Individual stream recordingsMKV (per participant)Minutes after session ends
Transcoded composite videoMP4 (single file per segment)10–45 minutes post-session (depends on queue)
Live-recording fileMP4Minutes after session ends

Retrieve file URLs via the Video API Archives route, or configure automatic delivery to your FTP/S3/Azure/GDrive storage via Portal. EnableX sends a webhook notification when files are ready.

Hard Mute Controls

Hard Mute / Unmute a Room

Hard muting silences all participants in the room. Any new participant joining a hard-muted room is automatically muted. Participants cannot unmute themselves — only the moderator can lift the hard mute.

// Mute the entire room
room.hardMute(function(result, error) {
  // result: { result: 0, msg: "Room is muted" }
});

room.addEventListener("hard-mute-room", function(res) {
  showRoomMutedBanner();
  // res.moderator — who hard-muted the room
});

// Unmute the entire room
room.hardUnmute(function(result, error) {
  // result: { result: 0, msg: "Room is un-muted" }
});

room.addEventListener("hard-unmute-room", function(res) {
  hideRoomMutedBanner();
});

Hard Mute / Unmute a Participant

Moderators can silence a specific participant's audio or video, or both. The affected participant cannot unmute themselves until the moderator lifts it.

// Mute a specific participant's audio
room.hardMuteUserAudio(targetClientId, function(response) {
  if (response.result === 0) console.log("Participant audio muted");
});

// Mute a specific participant's video
room.hardMuteUserVideo(targetClientId, function(response) {
  if (response.result === 0) console.log("Participant video muted");
});

// The affected participant receives:
room.addEventListener("hardmute-user-audio", function(event) {
  showForceMutedIndicator();
  // event.moderator — who muted you
});

room.addEventListener("hardmute-user-video", function(event) {
  showForcedVideoOffIndicator();
});

// Unmute
room.hardUnmuteUserAudio(targetClientId, function(response) {});
room.hardUnmuteUserVideo(targetClientId, function(response) {});
Room Controls

Lock / Unlock a Room

Locking prevents new participants from joining an ongoing session. Existing participants are unaffected. Only the moderator can lock or unlock.

room.lock();

room.addEventListener("room-locked", function(event) {
  showLockedRoomBadge();
  // event carries the moderator ID who locked the room
});

room.unlock();

room.addEventListener("room-unlocked", function(event) {
  hideLockedRoomBadge();
});

Moderated Entry (Knock-Enabled Rooms)

In knock-enabled rooms, participants wait for moderator approval before entering. The moderator receives a user-awaited event for each waiting participant and can approve or deny individually.

// Moderator is notified when someone is waiting
room.addEventListener("user-awaited", function(event, user) {
  // user.clientId, user.name — who is waiting
  showApprovalPrompt(user);
});

function approveUser(clientId) {
  room.approveAwaitedUser(clientId, function(success, error) {
    console.log("User approved:", clientId);
  });
}

function denyUser(clientId) {
  room.denyAwaitedUser(clientId, function(success, error) {
    console.log("User denied:", clientId);
  });
}

// The waiting participant receives this when approved:
room.addEventListener("room-allowed", function(event) {
  // Now proceed to publish stream and join the session
  publishStream();
});
Late-joining moderator

If participants are already waiting when the moderator joins, inspect room.awaitedParticipants immediately after the room-connected event. It is an array of { clientId, name } objects for all pending approvals.

Switch Room Mode

A room defined in group mode can be switched to lecture mode at runtime, and vice versa, without ending the session. When switching to lecture mode, all participant streams are dropped and floor access control activates. When switching back to group mode, participants are notified to republish their streams.

// Switch to lecture mode
room.switchRoomMode("lecture", function(resp) {
  if (resp.result === 0) console.log("Switched to lecture mode");
});

room.addEventListener("room-mode-switched", function(event) {
  var newMode = event.message.mode;  // "lecture" or "group"

  if (newMode === "lecture") {
    // Participant streams are dropped; floor access control is now active
    showLectureModeUI();
  } else {
    // Prompt participants to republish streams
    showPublishPrompt();
  }
});
User Management

Drop (Force-Disconnect) a User

The moderator can forcibly disconnect one or more participants. Pass an array of client IDs, or an empty array to disconnect everyone.

// Drop a specific participant
room.dropUser([targetClientId], function(res) {
  if (res.result === 0) console.log("User dropped");
});

// Drop all participants (moderator stays)
room.dropUser([], function(res) {});

// Affected participant receives:
room.addEventListener("room-disconnected", function(event) {
  if (event.message.cause.msg === "disconnected-by-moderator") {
    showDroppedMessage();
  }
});

Switch Participant Roles

A moderator can promote a participant to moderator role, and the former moderator becomes a participant. The newly appointed moderator immediately gains access to all moderator controls. Role switching can be chained — the new moderator can promote someone else.

room.switchUserRole(participantClientId, function(status) {
  if (status.result === 0) console.log("Role switched");
});

room.addEventListener("user-role-changed", function(event) {
  // event.moderator.new — new moderator's client ID
  // event.moderator.old — previous moderator's client ID
  // event.raisedHands   — floor requests (Lecture mode)
  // event.approvedHands — floor holders (Lecture mode)
  refreshModeratorControls(event);
});

Extend Session Duration

A session's duration is set at room creation. When 10 minutes remain, all participants are notified via conference-remaining-duration and can trigger an extension. The extension window closes once triggered; it reopens at 5 minutes if not extended.

room.addEventListener("conference-remaining-duration", function(event) {
  var minutesLeft = event.message.timeLeft;
  showExtensionPrompt(minutesLeft);
});

function requestExtension() {
  room.extendConferenceDuration(function(message) {
    if (message.result === 0) {
      var grantedMinutes = message.extendedTime;
      showExtensionConfirmed(grantedMinutes);
    }
  });
}

Destroy Session

Moderators call room.destroy() to immediately end the session for all connected participants. All users receive a room-disconnected event with reason "disconnected-by-moderator".

room.destroy();

// All participants receive this:
room.addEventListener("room-disconnected", function(event) {
  if (event.message.cause.msg === "disconnected-by-moderator") {
    showSessionEndedScreen();
  }
});
Pin & Spotlight

Pin a User

Pinned users appear in the Active Talker list at all times, regardless of whether they are speaking. They are placed after actively talking users in ascending order of activity. The pin limit is max_active_talkers – 1.

// Pin one or more participants
room.pinUsers([clientId1, clientId2], function(resp) {
  // resp: { result: 0, clients: ["clientId1", "clientId2"] }
});

room.unpinUsers([clientId1], function(resp) {});

room.addEventListener("updated-pinned-users", function(event) {
  // event.moderator_id — who updated pins
  // event.clientIds    — current list of pinned user IDs
  refreshPinnedIndicators(event.clientIds);
});

Spotlight a User

Spotlighting pushes a user to the top of the Active Talker list regardless of activity — they are always front-and-center in the grid. Unlike pinning, spotlighted users appear before actively talking users. Moderators can spotlight up to max_active_talkers users simultaneously.

// Spotlight a presenter
room.addSpotlightUsers([presenterClientId], function(resp) {
  // resp: { result: 0, clients: [...] }
});

room.removeSpotlightUsers([presenterClientId], function(resp) {});

room.addEventListener("spotlight-users", function(event) {
  // event.moderator_id — who changed spotlight
  // event.users        — current spotlighted user IDs
  refreshSpotlightHighlights(event.users);
});
Spotlight vs. Pin

Spotlight places users at the very top of the Active Talker list — above even actively speaking users. Pin keeps users in the list but below active speakers. Use spotlight for a presenter who must always be prominent; use pin for "always visible" participants who shouldn't dominate the top slot.

← In-Session Communication Breakout Rooms →