Loading...
Loading...
Compare original and translation side by side
| Role | role_type | Integration | JWT Required | Purpose |
|---|---|---|---|---|
| Customer | 1 | Website integration (CDN or npm) | Yes | User who shares their browser session |
| Agent | 2 | Iframe (CDN) or npm (BYOP only) | Yes | Support staff who views/assists customer |
| 角色 | role_type | 集成方式 | 是否需要JWT | 用途 |
|---|---|---|---|---|
| 客户 | 1 | 网站集成(CDN或npm) | 是 | 共享浏览器会话的用户 |
| 坐席 | 2 | Iframe(CDN)或npm(仅BYOP模式) | 是 | 为客户提供查看/协助的支持人员 |
pincode_updatedPincode is not found30308pincode_updatedPincode is not found30308role_type=1role_type=2role_type=1role_type=2*.zoom.us*.zoom.us| Credential | Type | Used For | Exposure Safe? |
|---|---|---|---|
| SDK Key | Public | CDN URL, JWT | ✓ Yes (client-side) |
| SDK Secret | Private | Sign JWTs | ✗ No (server-side only) |
| API Key | Private | REST API calls (optional) | ✗ No (server-side only) |
| API Secret | Private | REST API calls (optional) | ✗ No (server-side only) |
| 凭证 | 类型 | 用途 | 是否可安全暴露? |
|---|---|---|---|
| SDK Key | 公开 | CDN URL、JWT | ✓ 是(客户端可用) |
| SDK Secret | 私密 | 签名JWT | ✗ 否(仅服务端可用) |
| API Key | 私密 | REST API调用(可选) | ✗ 否(仅服务端可用) |
| API Secret | 私密 | REST API调用(可选) | ✗ 否(仅服务端可用) |
git clone https://github.com/zoom/cobrowsesdk-auth-endpoint-sample.git
cd cobrowsesdk-auth-endpoint-sample
npm installgit clone https://github.com/zoom/cobrowsesdk-auth-endpoint-sample.git
cd cobrowsesdk-auth-endpoint-sample
npm install
**Token endpoint:**
```javascript
// POST https://YOUR_TOKEN_SERVICE_BASE_URL
{
"role": 1, // 1 = customer, 2 = agent
"userId": "user123",
"userName": "John Doe"
}
// Response
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
**Token端点**:```javascript
// POST https://YOUR_TOKEN_SERVICE_BASE_URL
{
"role": 1, // 1 = 客户, 2 = 坐席
"userId": "user123",
"userName": "John Doe"
}
// 返回结果
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}<!DOCTYPE html>
<html>
<head>
<title>Customer - Cobrowse Demo</title>
<script type="module">
const ZOOM_SDK_KEY = 'YOUR_SDK_KEY';
// Load SDK from CDN
(function(r, a, b, f, c, d) {
r[f] = r[f] || { init: function() { r.ZoomCobrowseSDKInitArgs = arguments }};
var fragment = a.createDocumentFragment();
function loadJs(url) {
c = a.createElement(b);
d = a.getElementsByTagName(b)[0];
c["async"] = false;
c.src = url;
fragment.appendChild(c);
}
loadJs(`https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js/2.13.2`);
d.parentNode.insertBefore(fragment, d);
})(window, document, "script", "ZoomCobrowseSDK");
</script>
</head>
<body>
<h1>Customer Support</h1>
<button id="cobrowse-btn" disabled>Loading...</button>
<!-- Sensitive fields - will be masked from agent -->
<label>SSN: <input type="text" class="pii-mask" placeholder="XXX-XX-XXXX"></label>
<label>Credit Card: <input type="text" class="pii-mask" placeholder="XXXX-XXXX-XXXX-XXXX"></label>
<script type="module">
let sessionRef = null;
const settings = {
allowAgentAnnotation: true,
allowCustomerAnnotation: true,
piiMask: {
maskCssSelectors: ".pii-mask",
maskType: "custom_input"
}
};
ZoomCobrowseSDK.init(settings, function({ success, session, error }) {
if (success) {
sessionRef = session;
// Listen for PIN code
session.on("pincode_updated", (payload) => {
console.log("PIN Code:", payload.pincode);
// IMPORTANT: this is the PIN agent should use
alert(`Share this PIN with agent: ${payload.pincode}`);
});
// Listen for session events
session.on("session_started", () => console.log("Session started"));
session.on("agent_joined", () => console.log("Agent joined"));
session.on("agent_left", () => console.log("Agent left"));
session.on("session_ended", () => console.log("Session ended"));
document.getElementById("cobrowse-btn").disabled = false;
document.getElementById("cobrowse-btn").innerText = "Start Cobrowse Session";
} else {
console.error("SDK init failed:", error);
}
});
document.getElementById("cobrowse-btn").addEventListener("click", async () => {
// Fetch JWT from your server
const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
role: 1,
userId: "customer_" + Date.now(),
userName: "Customer"
})
});
const { token } = await response.json();
// Start cobrowse session
sessionRef.start({ sdkToken: token });
});
</script>
</body>
</html><!DOCTYPE html>
<html>
<head>
<title>客户 - Cobrowse演示</title>
<script type="module">
const ZOOM_SDK_KEY = 'YOUR_SDK_KEY';
// 从CDN加载SDK
(function(r, a, b, f, c, d) {
r[f] = r[f] || { init: function() { r.ZoomCobrowseSDKInitArgs = arguments }};
var fragment = a.createDocumentFragment();
function loadJs(url) {
c = a.createElement(b);
d = a.getElementsByTagName(b)[0];
c["async"] = false;
c.src = url;
fragment.appendChild(c);
}
loadJs(`https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js/2.13.2`);
d.parentNode.insertBefore(fragment, d);
})(window, document, "script", "ZoomCobrowseSDK");
</script>
</head>
<body>
<h1>客户支持</h1>
<button id="cobrowse-btn" disabled>加载中...</button>
<!-- 敏感字段 - 对坐席屏蔽 -->
<label>SSN: <input type="text" class="pii-mask" placeholder="XXX-XX-XXXX"></label>
<label>信用卡: <input type="text" class="pii-mask" placeholder="XXXX-XXXX-XXXX-XXXX"></label>
<script type="module">
let sessionRef = null;
const settings = {
allowAgentAnnotation: true,
allowCustomerAnnotation: true,
piiMask: {
maskCssSelectors: ".pii-mask",
maskType: "custom_input"
}
};
ZoomCobrowseSDK.init(settings, function({ success, session, error }) {
if (success) {
sessionRef = session;
// 监听PIN码事件
session.on("pincode_updated", (payload) => {
console.log("PIN码:", payload.pincode);
// 重要: 这是坐席应该使用的PIN
alert(`将此PIN分享给坐席: ${payload.pincode}`);
});
// 监听会话事件
session.on("session_started", () => console.log("会话已启动"));
session.on("agent_joined", () => console.log("坐席已加入"));
session.on("agent_left", () => console.log("坐席已离开"));
session.on("session_ended", () => console.log("会话已结束"));
document.getElementById("cobrowse-btn").disabled = false;
document.getElementById("cobrowse-btn").innerText = "启动协同浏览会话";
} else {
console.error("SDK初始化失败:", error);
}
});
document.getElementById("cobrowse-btn").addEventListener("click", async () => {
// 从你的服务端获取JWT
const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
role: 1,
userId: "customer_" + Date.now(),
userName: "客户"
})
});
const { token } = await response.json();
// 启动协同浏览会话
sessionRef.start({ sdkToken: token });
});
</script>
</body>
</html><!DOCTYPE html>
<html>
<head>
<title>Agent Portal</title>
</head>
<body>
<h1>Agent Portal</h1>
<iframe
id="agent-iframe"
width="1024"
height="768"
allow="autoplay *; camera *; microphone *; display-capture *; geolocation *;"
></iframe>
<script>
async function connectAgent() {
// Fetch JWT from your server
const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
role: 2,
userId: "agent_" + Date.now(),
userName: "Support Agent"
})
});
const { token } = await response.json();
// Load Zoom agent portal
const iframe = document.getElementById("agent-iframe");
iframe.src = `https://us01-zcb.zoom.us/sdkapi/zcb/frame-templates/desk?access_token=${token}`;
}
connectAgent();
</script>
</body>
</html><!DOCTYPE html>
<html>
<head>
<title>坐席门户</title>
</head>
<body>
<h1>坐席门户</h1>
<iframe
id="agent-iframe"
width="1024"
height="768"
allow="autoplay *; camera *; microphone *; display-capture *; geolocation *;"
></iframe>
<script>
async function connectAgent() {
// 从你的服务端获取JWT
const response = await fetch("https://YOUR_TOKEN_SERVICE_BASE_URL", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
role: 2,
userId: "agent_" + Date.now(),
userName: "支持坐席"
})
});
const { token } = await response.json();
// 加载Zoom坐席门户
const iframe = document.getElementById("agent-iframe");
iframe.src = `https://us01-zcb.zoom.us/sdkapi/zcb/frame-templates/desk?access_token=${token}`;
}
connectAgent();
</script>
</body>
</html>const settings = {
allowAgentAnnotation: true, // Agent can draw
allowCustomerAnnotation: true // Customer can draw
};const settings = {
allowAgentAnnotation: true, // 坐席可绘制
allowCustomerAnnotation: true // 客户可绘制
};const settings = {
piiMask: {
maskType: "custom_input", // Mask specific fields
maskCssSelectors: ".pii-mask, #ssn", // CSS selectors
maskHTMLAttributes: "data-sensitive=true" // HTML attributes
}
};const settings = {
piiMask: {
maskType: "custom_input", // 屏蔽指定字段
maskCssSelectors: ".pii-mask, #ssn", // CSS选择器
maskHTMLAttributes: "data-sensitive=true" // HTML属性
}
};const settings = {
remoteAssist: {
enable: true,
enableCustomerConsent: true, // Customer must approve
remoteAssistTypes: ['scroll_page'], // Only scroll supported
requireStopConfirmation: false // Confirmation when stopping
}
};const settings = {
remoteAssist: {
enable: true,
enableCustomerConsent: true, // 需要客户同意
remoteAssistTypes: ['scroll_page'], // 仅支持滚动
requireStopConfirmation: false // 停止时是否需要确认
}
};const settings = {
multiTabSessionPersistence: {
enable: true,
stateCookieKey: '$$ZCB_SESSION$$' // Cookie key (base64 encoded)
}
};const settings = {
multiTabSessionPersistence: {
enable: true,
stateCookieKey: '$$ZCB_SESSION$$' // Cookie键(base64编码)
}
};ZoomCobrowseSDKZoomCobrowseSDK.init(settings, callback)session.start({ sdkToken })pincode_updatedagent_joinedsession.end()ZoomCobrowseSDKZoomCobrowseSDK.init(settings, callback)session.start({ sdkToken })pincode_updatedagent_joinedsession.end()session_joinedsession_joinedZoomCobrowseSDK.init(settings, function({ success, session, error }) {
if (success) {
const sessionInfo = session.getSessionInfo();
// Check if session is recoverable
if (sessionInfo.sessionStatus === 'session_recoverable') {
session.join(); // Auto-rejoin previous session
} else {
// Start new session
session.start({ sdkToken });
}
}
});ZoomCobrowseSDK.init(settings, function({ success, session, error }) {
if (success) {
const sessionInfo = session.getSessionInfo();
// 检查会话是否可恢复
if (sessionInfo.sessionStatus === 'session_recoverable') {
session.join(); // 自动重新加入之前的会话
} else {
// 启动新会话
session.start({ sdkToken });
}
}
});// ❌ WRONG - Secret exposed in frontend
const jwt = signJWT(payload, 'YOUR_SDK_SECRET'); // Security risk!
// ✅ CORRECT - Secret stays on server
const response = await fetch('/api/token', {
method: 'POST',
body: JSON.stringify({ role: 1, userId, userName })
});
const { token } = await response.json();// ❌ 错误 - Secret暴露在前端
const jwt = signJWT(payload, 'YOUR_SDK_SECRET'); // 存在安全风险!
// ✅ 正确 - Secret保留在服务端
const response = await fetch('/api/token', {
method: 'POST',
body: JSON.stringify({ role: 1, userId, userName })
});
const { token } = await response.json();| Credential | Used For | JWT Claim |
|---|---|---|
| SDK Key | CDN URL, JWT | |
| API Key | REST API calls (optional) | Not used in JWT |
app_key| 凭证 | 用途 | JWT声明 |
|---|---|---|
| SDK Key | CDN URL、JWT | |
| API Key | REST API调用(可选) | 不用于JWT |
app_key| Limit | Value | What Happens |
|---|---|---|
| Customers per session | 1 | Error 1012: |
| Agents per session | 5 | Error 1013: |
| Active sessions per browser | 1 | Error 1004: |
| PIN code length | 10 chars max | Error 1008: |
| 限制 | 数值 | 触发结果 |
|---|---|---|
| 每个会话的客户数 | 1 | 错误1012: |
| 每个会话的坐席数 | 5 | 错误1013: |
| 每个浏览器的活跃会话数 | 1 | 错误1004: |
| PIN码最大长度 | 10个字符 | 错误1008: |
| Event | Timeout | What Happens |
|---|---|---|
| Agent waiting for customer | 3 minutes | Session ends automatically |
| Page refresh reconnection | 2 minutes | Session ends if not reconnected |
| Reconnection attempts | 2 times max | Session ends after 2 failed attempts |
| 事件 | 超时时间 | 触发结果 |
|---|---|---|
| 坐席等待客户 | 3分钟 | 会话自动结束 |
| 页面刷新重连 | 2分钟 | 未重连则会话结束 |
| 重连尝试次数 | 最多2次 | 2次失败后会话结束 |
| Method | Use Case | Agent Integration | BYOP Required |
|---|---|---|---|
| CDN | Most use cases | Zoom-hosted iframe | No (auto PIN) |
| npm | Custom agent UI, full control | Custom npm integration | Yes (required) |
| 方式 | 适用场景 | 坐席集成 | 是否需要BYOP |
|---|---|---|---|
| CDN | 大多数场景 | Zoom托管的iframe | 否(自动生成PIN) |
| npm | 自定义坐席UI、完全可控 | 自定义npm集成 | 是(必须) |
<script>
const ZOOM_SDK_KEY = "YOUR_SDK_KEY_HERE";
(function(r,a,b,f,c,d){r[f]=r[f]||{init:function(){r.ZoomCobrowseSDKInitArgs=arguments}};
var fragment=a.createDocumentFragment();function loadJs(url) {c=a.createElement(b);d=a.getElementsByTagName(b)[0];c.async=false;c.src=url;fragment.appendChild(c);};
loadJs('https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js');d.parentNode.insertBefore(fragment,d);})(window,document,'script','ZoomCobrowseSDK');
</script><script>
const ZOOM_SDK_KEY = "YOUR_SDK_KEY_HERE";
(function(r,a,b,f,c,d){r[f]=r[f]||{init:function(){r.ZoomCobrowseSDKInitArgs=arguments}};
var fragment=a.createDocumentFragment();function loadJs(url) {c=a.createElement(b);d=a.getElementsByTagName(b)[0];c.async=false;c.src=url;fragment.appendChild(c);};
loadJs('https://us01-zcb.zoom.us/static/resource/sdk/${ZOOM_SDK_KEY}/js');d.parentNode.insertBefore(fragment,d);})(window,document,'script','ZoomCobrowseSDK');
</script><img><img>SKILL.mdSKILL.mdcobrowse-sdk/
├── SKILL.md # Main skill entry point
├── SKILL.md # This file - complete navigation
├── get-started.md # Step-by-step setup guide
│
├── concepts/ # Core concepts
│ ├── two-roles-pattern.md
│ ├── session-lifecycle.md
│ ├── jwt-authentication.md
│ └── distribution-methods.md
│
├── examples/ # Working examples
│ ├── customer-integration.md
│ ├── agent-integration.md
│ ├── annotations.md
│ ├── privacy-masking.md
│ ├── remote-assist.md
│ ├── multi-tab-persistence.md
│ ├── byop-custom-pin.md
│ ├── session-events.md
│ └── auto-reconnection.md
│
├── references/ # API and config references
│ ├── api-reference.md # SDK methods
│ ├── settings-reference.md # Init settings
│ ├── session-events.md # Event types
│ ├── error-codes.md # Error reference
│ ├── get-started.md # Official docs (crawled)
│ ├── features.md # Official docs (crawled)
│ ├── authorization.md # Official docs (crawled)
│ └── api.md # API docs (crawled)
│
└── troubleshooting/ # Problem resolution
├── common-issues.md
├── error-codes.md
├── cors-csp.md
└── browser-compatibility.mdcobrowse-sdk/
├── SKILL.md # 主技能入口
├── SKILL.md # 本文件 - 完整导航
├── get-started.md # 分步设置指南
│
├── concepts/ # 核心概念
│ ├── two-roles-pattern.md
│ ├── session-lifecycle.md
│ ├── jwt-authentication.md
│ └── distribution-methods.md
│
├── examples/ # 可运行示例
│ ├── customer-integration.md
│ ├── agent-integration.md
│ ├── annotations.md
│ ├── privacy-masking.md
│ ├── remote-assist.md
│ ├── multi-tab-persistence.md
│ ├── byop-custom-pin.md
│ ├── session-events.md
│ └── auto-reconnection.md
│
├── references/ # API与配置参考
│ ├── api-reference.md # SDK方法
│ ├── settings-reference.md # 初始化配置
│ ├── session-events.md # 事件类型
│ ├── error-codes.md # 错误参考
│ ├── get-started.md # 官方文档(爬取)
│ ├── features.md # 官方文档(爬取)
│ ├── authorization.md # 官方文档(爬取)
│ └── api.md # API文档(爬取)
│
└── troubleshooting/ # 问题解决
├── common-issues.md
├── error-codes.md
├── cors-csp.md
└── browser-compatibility.md.env.env