File: C:/Users/fred/.codex/.tmp/plugins/plugins/zoom/skills/video-sdk/web/examples/command-channel.md
# Command Channel
Complete working code for custom command messaging between participants on Web.
---
## Overview
The command channel enables custom data exchange between participants within the same session. Use cases:
- Application-specific signaling
- Session transfer / waiting room coordination
- Real-time collaboration data
- Custom control messages
```
+-------------------------------------------------------------------+
| COMMAND CHANNEL FLOW (Web) |
+-------------------------------------------------------------------+
| Setup: |
| client.join() -> client.getCommandClient() |
| |
| Sender: |
| cmdClient.send(message) [broadcast to all] |
| cmdClient.send(message, userId) [targeted] |
| |
| Receiver: |
| client.on('command-channel-message', callback) |
| |
| IMPORTANT: Command channel is SESSION-SCOPED. |
| It does NOT span across different sessions. |
+-------------------------------------------------------------------+
```
**Key difference from native SDKs**: Web uses `getCommandClient()` (NOT `getCmdChannel()` like Linux/Windows native SDKs).
---
## Limitations
| Limit | Value |
|-------|-------|
| Max message rate | 60 messages/second |
| Max message size | ~1KB recommended |
| Reliability | Best effort (not guaranteed) |
| Scope | Same session only |
**Note**: Commands are not persisted - late joiners won't receive previous commands.
---
## Setup Order (Critical)
You MUST call `getCommandClient()` and register listeners AFTER `client.join()`. The command channel subsystem is not active until after joining a session.
```javascript
// CORRECT: Everything after join
await client.join(topic, jwt, userName);
const cmdClient = client.getCommandClient();
client.on('command-channel-status', (status) => { /* ... */ });
client.on('command-channel-message', (payload) => { /* ... */ });
```
```javascript
// WRONG: Registering before join — command channel not active yet
await client.init('en-US', 'Global', { patchJsMedia: true });
client.on('command-channel-message', handler); // WILL NOT RECEIVE MESSAGES
await client.join(topic, jwt, userName);
```
Without calling `getCommandClient()` after join, the command channel may never connect and `command-channel-status` will never fire.
---
## Complete Working Example
```javascript
import ZoomVideo from '@zoom/videosdk';
const client = ZoomVideo.createClient();
async function joinAndSetupCommandChannel(topic, jwt, userName) {
// 1. Initialize
await client.init('en-US', 'Global', { patchJsMedia: true });
// 2. Join session
await client.join(topic, jwt, userName);
// 3. Activate command channel (MUST be after join)
const cmdClient = client.getCommandClient();
// 4. Listen for connection status
client.on('command-channel-status', (status) => {
console.log('Command channel status:', status);
if (status === true) {
console.log('Command channel connected - ready to send');
}
});
// 5. Listen for incoming commands
client.on('command-channel-message', (payload) => {
try {
const data = JSON.parse(payload.text);
console.log('Command received:', data);
handleCommand(data);
} catch (e) {
console.log('Raw command received:', payload.text);
}
});
return cmdClient;
}
// Send a command (broadcast to all participants in session)
function sendCommand(cmdClient, type, data) {
const message = JSON.stringify({ type, data });
cmdClient.send(message);
}
// Send to a specific participant
function sendCommandToUser(cmdClient, userId, type, data) {
const message = JSON.stringify({ type, data });
cmdClient.send(message, userId);
}
// Handle incoming commands
function handleCommand(data) {
switch (data.type) {
case 'ping':
console.log('Received ping');
break;
case 'transfer':
console.log('Transfer info:', data);
break;
default:
console.log('Unknown command type:', data.type);
}
}
```
---
## Events Reference
### command-channel-status
Fires when the command channel connection status changes.
```javascript
client.on('command-channel-status', (status) => {
// status: boolean - true when connected, false when disconnected
if (status) {
// Safe to send commands now
}
});
```
### command-channel-message
Fires when a command is received from another participant.
```javascript
client.on('command-channel-message', (payload) => {
// payload.text: string - the command message content
// payload.senderId: number - user ID of the sender
const message = payload.text;
console.log('Received:', message);
});
```
---
## Cross-Platform Compatibility
When sending commands between Web and Linux/Windows native SDKs:
| Property | Web SDK | Linux SDK | Windows SDK |
|----------|---------|-----------|-------------|
| Get channel | `getCommandClient()` | `getCmdChannel()` | `getCmdChannel()` |
| Send broadcast | `cmdClient.send(msg)` | `sendCommand(nullptr, msg)` | `sendCommand(NULL, msg)` |
| Send targeted | `cmdClient.send(msg, userId)` | `sendCommand(user, msg)` | `sendCommand(user, msg)` |
| String type | JavaScript string | `const char*` (UTF-8) | `const wchar_t*` (wide) |
| Receive event | `command-channel-message` | `onCommandReceived` callback | `onCommandReceived` callback |
Use JSON strings as the message format for cross-platform compatibility.
---
## Common Issues
### Commands Not Received
**Cause**: `getCommandClient()` not called after join, or listeners registered before join
**Fix**: Ensure setup order is correct — join first, then getCommandClient(), then register listeners.
### command-channel-status Never Fires
**Cause**: `getCommandClient()` was not called
**Fix**: You must explicitly call `client.getCommandClient()` after join to activate the channel.
### Cross-SDK Messages Not Arriving
**Cause**: Version mismatch or timing issues between different SDK platforms
**Fix**: Add a server-side polling fallback:
```javascript
const pollTimer = setInterval(async () => {
const res = await fetch(`/api/status/${sessionName}`);
const data = await res.json();
if (data.ready) {
clearInterval(pollTimer);
handleData(data);
}
}, 3000);
```
---
## Related Documentation
- [Session Join Pattern](session-join-pattern.md) - Session setup
- [Event Handling](event-handling.md) - Event listener patterns
- [Linux Command Channel](../../linux/examples/command-channel.md) - Linux native equivalent
- [Windows Command Channel](../../windows/examples/command-channel.md) - Windows native equivalent
- [Authorization](../../references/authorization.md) - JWT roleType for host/co-host