diff --git a/src/content/docs/realtime/realtimekit/core/end-a-session.mdx b/src/content/docs/realtime/realtimekit/core/end-a-session.mdx
index 7b84034fc074a33..24b35c0344c21ab 100644
--- a/src/content/docs/realtime/realtimekit/core/end-a-session.mdx
+++ b/src/content/docs/realtime/realtimekit/core/end-a-session.mdx
@@ -3,7 +3,7 @@ pcx_content_type: how-to
title: End a session
slug: realtime/realtimekit/core/end-a-session
sidebar:
- order: 6
+ order: 7
---
import { Render } from "~/components";
diff --git a/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx b/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx
new file mode 100644
index 000000000000000..3e4d70fbae687a3
--- /dev/null
+++ b/src/content/docs/realtime/realtimekit/core/manage-other-participants-in-a-session.mdx
@@ -0,0 +1,245 @@
+---
+title: Manage other participants in a session
+pcx_content_type: how-to
+slug: realtime/realtimekit/core/manage-other-participants-in-a-session
+sidebar:
+ order: 6
+---
+
+import { Render } from "~/components";
+import RTKSDKSelector from "~/components/realtimekit/RTKSDKSelector/RTKSDKSelector.astro";
+import RTKCodeSnippet from "~/components/realtimekit/RTKCodeSnippet/RTKCodeSnippet.astro";
+
+Use RealtimeKit host controls to manage other participants in a live session. You can mute audio or video, pin a participant, or remove participants from the session.
+These actions require specific host control permissions enabled in the local participant's [Preset](/realtime/realtimekit/concepts/preset/).
+Before you show UI controls or call these methods, verify that the local participant has the necessary permissions.
+In this guide, the **local participant** refers to the user performing the actions.
+
+
+
+## Before you start
+
+### Prerequisites
+
+- The local participant (for example, a host or moderator) must have the required **Host Controls** permissions enabled in their preset. For details, refer to [Preset](/realtime/realtimekit/concepts/preset/).
+
+### Select a remote participant
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Mute audio
+
+Mute audio of participants when you need to manage background noise, moderate a classroom or webinar, or prevent interruptions during a session.
+This action requires the **Mute Audio** (`disable_participant_audio`) host control permission enabled in the local participant's preset.
+
+### Mute a participant
+
+To mute a specific participant's audio:
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Mute all participants
+
+This affects all participants, including the local participant. To mute audio for all participants in the session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Disable video
+
+Disable video of participants when you need to moderate a session, enforce privacy, or prevent unwanted video during a classroom or webinar.
+This action requires the **Mute Video** (`disable_participant_video`) host control permission enabled in the local participant's preset.
+
+### Disable video for a participant
+
+To disable a specific participant's video:
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Disable video for all participants
+
+This affects all participants, including the local participant. To disable video for all participants in the session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Pin participants
+
+Pin a participant to highlight them, such as a webinar presenter or classroom teacher. This is a session-wide action. All participants will see the pinned participant as the focus.
+This action requires the **Pin Participant** (`pin_participant`) host control permission enabled in the local participant's preset.
+
+:::note
+Only one participant can be pinned at a time. Pinning a new participant automatically unpins the previous one.
+:::
+
+### Pin a participant
+
+To pin a participant in a session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Unpin a participant
+
+Unpin a participant when you need to undo the highlight and return the session to a standard grid or active speaker view.
+To unpin a pinned participant in a session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Remove participants
+
+Remove participants from the session when you need to moderate disruptive behavior or enforce session rules.
+This action requires the **Kick Participants** (`kick_participant`) host control permission enabled in the local participant's preset.
+
+### Remove a participant
+
+To remove a specific participant from the session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Remove all participants
+
+This removes everyone from the session, including the local participant. This ends the session for everyone.
+
+For a complete end-a-session flow, refer to [End a session](/realtime/realtimekit/core/end-a-session/).
+
+To remove all participants from the session:
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Next steps
+
+- Review how presets control permissions in [Preset](/realtime/realtimekit/concepts/preset/).
+- Review error handling details in [Error Codes](/realtime/realtimekit/core/error-codes/).
diff --git a/src/content/docs/realtime/realtimekit/core/stage-management.mdx b/src/content/docs/realtime/realtimekit/core/stage-management.mdx
index 4c583b933f525e7..3d76f7f43b11f6a 100644
--- a/src/content/docs/realtime/realtimekit/core/stage-management.mdx
+++ b/src/content/docs/realtime/realtimekit/core/stage-management.mdx
@@ -3,7 +3,7 @@ pcx_content_type: navigation
title: Stage Management
slug: realtime/realtimekit/core/stage-management
sidebar:
- order: 8
+ order: 9
---
import { Tabs, TabItem } from "~/components";
diff --git a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx
index 3c69bff392523da..6491c5d9318a4c0 100644
--- a/src/content/docs/realtime/realtimekit/core/waiting-room.mdx
+++ b/src/content/docs/realtime/realtimekit/core/waiting-room.mdx
@@ -3,7 +3,7 @@ pcx_content_type: navigation
title: Waiting Room
slug: realtime/realtimekit/core/waiting-room
sidebar:
- order: 7
+ order: 8
---
import { Tabs, TabItem } from "~/components";
diff --git a/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx b/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx
new file mode 100644
index 000000000000000..f0e6ffe776a0886
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/disable-all-participants-video-steps.mdx
@@ -0,0 +1,61 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to disable other participants' video.
+
+ ```ts
+ const canDisableVideo =
+ meeting.self.permissions.canDisableParticipantVideo === true;
+ if (!canDisableVideo) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `disableAllVideo()`.
+
+ If the local participant does not have the required permission, `disableAllVideo()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await meeting.participants.disableAllVideo();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to disable other participants’ video.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds, each participant’s `videoEnabled` becomes `false`, and the SDK emits a `videoUpdate` event.
+ The local participant also receives `videoUpdate` on `meeting.self`.
+
+ Listen to remote participant updates on the `joined` map:
+
+ ```ts
+ meeting.participants.joined.on(
+ "videoUpdate",
+ (participant, { videoEnabled, videoTrack }) => {
+ // videoEnabled is false
+ // Update UI for the participant
+ },
+ );
+ ```
+
+ Listen to local participant update on `meeting.self`:
+
+ ```ts
+ meeting.self.on("videoUpdate", ({ videoEnabled, videoTrack }) => {
+ // videoEnabled is false
+ // Update UI for the local participant
+ });
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx b/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx
new file mode 100644
index 000000000000000..d8dd59748e78e99
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/disable-participant-video-steps.mdx
@@ -0,0 +1,60 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to disable other participants' video.
+
+ ```ts
+ const canDisableVideo =
+ meeting.self.permissions.canDisableParticipantVideo === true;
+ if (!canDisableVideo) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `disableVideo()` on the target participant.
+
+ If the local participant does not have the required permission, `disableVideo()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await participant.disableVideo();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to disable other participants’ video.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds, the target participant's `videoEnabled` becomes `false`, and the SDK emits a `videoUpdate` event.
+
+ **Option A**: Listen on the participant object
+
+ ```ts
+ participant.on("videoUpdate", ({ videoEnabled, videoTrack }) => {
+ // videoEnabled is false
+ // Update UI for the participant
+ });
+ ```
+
+ **Option B**: Listen on the `joined` map
+
+ ```ts
+ meeting.participants.joined.on(
+ "videoUpdate",
+ (participant, { videoEnabled, videoTrack }) => {
+ // videoEnabled is false
+ // Update UI for the participant
+ },
+ );
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx b/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx
new file mode 100644
index 000000000000000..d92e2f0ac0c3488
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/mute-all-participants-steps.mdx
@@ -0,0 +1,61 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to mute other participants' audio.
+
+ ```ts
+ const canMuteAudio =
+ meeting.self.permissions.canDisableParticipantAudio === true;
+ if (!canMuteAudio) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `disableAllAudio()`.
+
+ If the local participant does not have the required permission, `disableAllAudio()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await meeting.participants.disableAllAudio();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to mute other participants’ audio.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds, each participant’s `audioEnabled` becomes `false`, and the SDK emits an `audioUpdate` event.
+ The local participant also receives `audioUpdate` on `meeting.self`.
+
+ Listen to remote participant updates on the `joined` map:
+
+ ```ts
+ meeting.participants.joined.on(
+ "audioUpdate",
+ (participant, { audioEnabled, audioTrack }) => {
+ // audioEnabled is false
+ // Update UI for the participant
+ },
+ );
+ ```
+
+ Listen to the local participant update on `meeting.self`:
+
+ ```ts
+ meeting.self.on("audioUpdate", ({ audioEnabled, audioTrack }) => {
+ // audioEnabled is false
+ // Update UI for the local participant
+ });
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx
new file mode 100644
index 000000000000000..4463534ba95ac11
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/mute-participant-steps.mdx
@@ -0,0 +1,62 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to mute other participants' audio.
+
+ ```ts
+ const canMuteAudio =
+ meeting.self.permissions.canDisableParticipantAudio === true;
+ if (!canMuteAudio) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `disableAudio()` on the target participant.
+
+ If the local participant does not have the required permission, `disableAudio()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await participant.disableAudio();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to mute other participants’ audio.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds, the target participant's `audioEnabled` becomes `false`, and the SDK emits an `audioUpdate` event.
+
+ **Option A**: Listen on the participant object
+
+ ```ts
+ participant.on("audioUpdate", ({ audioEnabled, audioTrack }) => {
+ // audioEnabled is false
+ // Update UI for the participant
+ });
+ ```
+
+ **Option B**: Listen on the `joined` map
+
+ ```ts
+ meeting.participants.joined.on(
+ "audioUpdate",
+ (participant, { audioEnabled, audioTrack }) => {
+ if (participant.id === targetParticipantId) {
+ // audioEnabled is false
+ // Update UI for the participant
+ }
+ },
+ );
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx
new file mode 100644
index 000000000000000..c11a34e11d7df07
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/pin-participant-steps.mdx
@@ -0,0 +1,69 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to pin participants.
+
+ ```ts
+ const canPinParticipant = meeting.self.permissions.pinParticipant === true;
+ if (!canPinParticipant) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `pin()` on the target participant.
+
+ If the local participant does not have the required permission, `pin()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await participant.pin();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to pin participants.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds:
+ - The target participant's `isPinned` becomes true.
+ - The participant is added to `meeting.participants.pinned`.
+ - The SDK emits a `pinned` event.
+
+ **Option A**: Listen on the participant object
+
+ ```ts
+ participant.on("pinned", (updatedParticipant) => {
+ // updatedParticipant.isPinned is true
+ // Update your UI.
+ });
+ ```
+
+ **Option B**: Listen on the `joined` map
+
+ ```ts
+ meeting.participants.joined.on("pinned", (updatedParticipant) => {
+ // updatedParticipant.isPinned is true
+ // Update your UI.
+ });
+ ```
+
+ If there was an existing pinned participant before, then the SDK emits an `unpinned` event for that participant.
+
+4. On the target pinned participant's side, `meeting.self.isPinned` becomes `true` and `meeting.self` emits `pinned`:
+
+ ```ts
+ meeting.self.on("pinned", (selfParticipant) => {
+ // Update the local UI to indicate the participant is pinned.
+ });
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx b/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx
new file mode 100644
index 000000000000000..61f2b240e32323d
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/remove-all-participants-steps.mdx
@@ -0,0 +1,46 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to remove participants.
+
+ ```ts
+ const canKickParticipant = meeting.self.permissions.kickParticipant === true;
+ if (!canKickParticipant) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `kickAll()`.
+
+ If the local participant does not have the required permission, `kickAll()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await meeting.participants.kickAll();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to remove participants.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds, all participants exit the session. On each client, `meeting.self` emits `roomLeft` with state set to `ended`.
+
+ ```ts
+ meeting.self.on("roomLeft", ({ state }) => {
+ if (state === "ended") {
+ // Show a message and navigate the user out of the meeting UI.
+ }
+ });
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx
new file mode 100644
index 000000000000000..98c575260f80a03
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/remove-participant-steps.mdx
@@ -0,0 +1,59 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to remove participants.
+
+ ```ts
+ const canKickParticipant = meeting.self.permissions.kickParticipant === true;
+ if (!canKickParticipant) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `kick()` on the target participant.
+
+ If the local participant does not have the required permission, `kick()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await participant.kick();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to remove participants.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds:
+ - The kicked participant is removed from `meeting.participants.joined`.
+ - The participant is removed from other participant maps they were in (for example, `meeting.participants.pinned`).
+ - The SDK emits `participantLeft` on `meeting.participants.joined`.
+
+ ```ts
+ meeting.participants.joined.on("participantLeft", (participant) => {
+ // Remove the participant tile from the UI.
+ });
+ ```
+
+ Other participants in the session also observe the participant leaving through `participantLeft`.
+
+4. On the removed participant's side, the session disconnects and `meeting.self` emits `roomLeft` event with state set to `kicked`.
+
+ ```ts
+ meeting.self.on("roomLeft", ({ state }) => {
+ if (state === "kicked") {
+ // Show a message and navigate the user out of the meeting UI.
+ }
+ });
+ ```
+
+
diff --git a/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx b/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx
new file mode 100644
index 000000000000000..c73d3efc5c4db5e
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/select-a-remote-participant.mdx
@@ -0,0 +1,15 @@
+---
+{}
+---
+
+To perform actions on a specific participant, you first need to retrieve their participant object.
+Remote participants (other participants) are available in `meeting.participants`. The local participant is available in `meeting.self`.
+Refer to [Meeting Object Explained](/realtime/realtimekit/core/meeting-object-explained/) for details.
+
+```ts
+const joinedParticipants = meeting.participants.joined.toArray();
+const participant = joinedParticipants[0];
+if (!participant) {
+ // No remote participants are currently joined.
+}
+```
diff --git a/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx b/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx
new file mode 100644
index 000000000000000..4c946dbcdd9ee8d
--- /dev/null
+++ b/src/content/partials/realtime/realtimekit/web/unpin-participant-steps.mdx
@@ -0,0 +1,67 @@
+---
+{}
+---
+
+import { Steps } from "~/components";
+
+
+
+1. Check that the local participant has permission to unpin participants.
+
+ ```ts
+ const canUnpinParticipant = meeting.self.permissions.pinParticipant === true;
+ if (!canUnpinParticipant) {
+ // Disable the control in your UI.
+ }
+ ```
+
+2. Call `unpin()` on the target participant.
+
+ If the local participant does not have the required permission, `unpin()` throws a `ClientError` with code `1201`.
+
+ ```ts
+ try {
+ await participant.unpin();
+ } catch (err: any) {
+ if (err?.code === 1201) {
+ // The local participant does not have permission to unpin participants.
+ return;
+ }
+ throw err;
+ }
+ ```
+
+3. Handle the result by listening for updates.
+
+ After the call succeeds:
+ - The target participant's `isPinned` becomes `false`.
+ - The participant is removed from `meeting.participants.pinned`.
+ - The SDK emits an `unpinned` event.
+
+ **Option A**: Listen on the participant object
+
+ ```ts
+ participant.on("unpinned", (updatedParticipant) => {
+ // updatedParticipant.isPinned is false
+ // Update your UI.
+ });
+ ```
+
+ **Option B**: Listen on the `joined` map
+
+ ```ts
+ meeting.participants.joined.on("unpinned", (updatedParticipant) => {
+ // updatedParticipant.isPinned is false
+ // Update your UI.
+ });
+ ```
+
+4. On the target unpinned participant's side, `meeting.self.isPinned` becomes `false` and `meeting.self` emits `unpinned`:
+
+ ```ts
+ meeting.self.on("unpinned", (selfParticipant) => {
+ // Update the local UI to indicate the participant is no longer pinned.
+ });
+ ```
+
+