partner-built/zoom-plugin/skills/cobrowse-sdk/SKILL.md
Reference skill for Zoom Cobrowse SDK. Use after routing to a collaborative-support workflow when implementing browser co-browsing, annotation tools, privacy masking, remote assist, or PIN-based session sharing.
npx skillsauth add anthropics/knowledge-work-plugins zoom-cobrowse-sdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Background reference for collaborative browsing on the web with Zoom Cobrowse SDK. Use this after the support workflow is clear and you need implementation detail.
Official Documentation: https://developers.zoom.us/docs/cobrowse-sdk/
API Reference: https://marketplacefront.zoom.us/sdk/cobrowse/
Quickstart Repository: https://github.com/zoom/CobrowseSDK-Quickstart
Auth Endpoint Sample: https://github.com/zoom/cobrowsesdk-auth-endpoint-sample
New to Cobrowse SDK? Follow this path:
Core Concepts:
Features:
Troubleshooting:
Reference:
The Zoom Cobrowse SDK is a JavaScript library that provides:
Cobrowse has two distinct roles, each with different integration patterns:
| 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 |
Key Insight: Customer and agent use different integration methods but the same JWT authentication pattern.
For customer/agent demos, treat the PIN from customer SDK event pincode_updated as the only user-facing PIN.
If these rules are ignored, agent desk often fails with Pincode is not found / code 30308.
This is the flow most teams implement first, and what users usually expect in demos:
role_type=1)
role_type=2)
If a demo only has one generic "session" user, it is incomplete for real cobrowse operations.
Supported Browsers:
Network Requirements:
*.zoom.usThird-Party Cookies:
Note: Cobrowse SDK is a feature of Video SDK (not a separate product).
You'll receive 4 credentials from Zoom Marketplace → Video SDK App → Cobrowse tab:
| Credential | Type | Used For | Exposure Safe? |
|------------|------|----------|----------------|
| SDK Key | Public | CDN URL, JWT app_key claim | ✓ 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) |
Critical: SDK Key is public (embedded in CDN URL), but SDK Secret must never be exposed client-side.
Deploy a server-side endpoint to generate JWTs. Use the official sample:
git clone https://github.com/zoom/cobrowsesdk-auth-endpoint-sample.git
cd cobrowsesdk-auth-endpoint-sample
npm install
# Create .env file
cat > .env << EOF
ZOOM_SDK_KEY=your_sdk_key_here
ZOOM_SDK_SECRET=your_sdk_secret_here
PORT=4000
EOF
npm start
Token endpoint:
// POST https://YOUR_TOKEN_SERVICE_BASE_URL
{
"role": 1, // 1 = customer, 2 = agent
"userId": "user123",
"userName": "John Doe"
}
// Response
{
"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>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>
Both customer and agent can draw on the shared screen:
const settings = {
allowAgentAnnotation: true, // Agent can draw
allowCustomerAnnotation: true // Customer can draw
};
Available tools:
Hide sensitive fields from agents using CSS selectors:
const settings = {
piiMask: {
maskType: "custom_input", // Mask specific fields
maskCssSelectors: ".pii-mask, #ssn", // CSS selectors
maskHTMLAttributes: "data-sensitive=true" // HTML attributes
}
};
Supported masking:
Agent can scroll the customer's page:
const settings = {
remoteAssist: {
enable: true,
enableCustomerConsent: true, // Customer must approve
remoteAssistTypes: ['scroll_page'], // Only scroll supported
requireStopConfirmation: false // Confirmation when stopping
}
};
Session continues when customer opens new tabs:
const settings = {
multiTabSessionPersistence: {
enable: true,
stateCookieKey: '$$ZCB_SESSION$$' // Cookie key (base64 encoded)
}
};
ZoomCobrowseSDKZoomCobrowseSDK.init(settings, callback)session.start({ sdkToken })pincode_updated event firesagent_joined event firessession.end() or agent leavessession_joined event firesWhen customer refreshes the page:
ZoomCobrowseSDK.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 });
}
}
});
Recovery window: 2 minutes. After 2 minutes, session ends.
Problem: Developers often accidentally embed SDK Secret in frontend code.
Solution:
// ❌ 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();
| Credential | Used For | JWT Claim |
|------------|----------|-----------|
| SDK Key | CDN URL, JWT app_key | app_key: "SDK_KEY" |
| API Key | REST API calls (optional) | Not used in JWT |
Common mistake: Using API Key instead of SDK Key in JWT app_key claim.
| Limit | Value | What Happens |
|-------|-------|--------------|
| Customers per session | 1 | Error 1012: SESSION_CUSTOMER_COUNT_LIMIT |
| Agents per session | 5 | Error 1013: SESSION_AGENT_COUNT_LIMIT |
| Active sessions per browser | 1 | Error 1004: SESSION_COUNT_LIMIT |
| PIN code length | 10 chars max | Error 1008: SESSION_PIN_INVALID_FORMAT |
| 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 |
Problem: SDK doesn't load on HTTP sites.
Solution:
Problem: Refresh reconnection doesn't work.
Solution: Enable third-party cookies in browser settings.
Affected scenarios:
| 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) |
Key Insight: If you want npm integration, you must use BYOP (Bring Your Own PIN) mode.
Problem: Cobrowse doesn't work in cross-origin iframes.
Solution: Inject SDK snippet into cross-origin iframes:
<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>
Same-origin iframes: No extra setup needed.
Not synchronized:
Partially synchronized:
Supported:
Not supported:
<img> elements ✗This skill includes comprehensive guides organized by category:
Need help? Start with Integrated Index section below for complete navigation.
This section was migrated from SKILL.md.
Complete navigation guide for all Cobrowse SDK documentation.
If you're new to Zoom Cobrowse SDK, follow this learning path:
Foundational concepts you need to understand:
Complete working examples for common scenarios:
Complete API and configuration references:
API Reference - All SDK methods and interfaces
Settings Reference - All initialization settings
Session Events Reference - All event types
Quick diagnostics and common issue resolution:
Common Issues - Quick fixes for frequent problems
Error Codes - Error code lookup and solutions
CORS and CSP - Cross-origin and Content Security Policy setup
Browser Compatibility - Browser requirements and limitations
Find documentation by what you're trying to do:
Set up cobrowse for the first time:
Add annotation tools:
Hide sensitive data from agents:
Let agents control customer's page:
Use custom PIN codes:
Handle page refreshes:
Integrate with npm (not CDN):
Debug session connection issues:
Configure CORS and CSP headers:
Quick lookup for error code solutions:
External documentation and samples:
cobrowse-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.md
Find by keyword:
Not finding what you need? Check the Official Documentation or ask on the Dev Forum.
.env keys and where to find each value.testing
Reads a forwarded customer email or ticket, pulls order/refund status from PayPal and account history from HubSpot, drafts a tone-matched reply in the owner's writing voice, and can issue a PayPal refund with explicit owner approval. Use when the user says "draft a response," "answer this customer," "where's my order," or "I want a refund."
development
Prepares tax-season materials for small business owners — framed as deliverables for their accountant, not tax advice. Two modes: (1) quarterly estimated tax calculation — pulls YTD net income from QuickBooks and calculates the federal income tax + self-employment tax liability and quarterly payment due; (2) year-end 1099 prep — scans QuickBooks, PayPal, and Stripe for contractors paid over $600, builds a 1099-NEC candidate list with missing W-9 flags, and produces a plain-English summary a CPA can work from directly. Trigger this skill whenever the user mentions: quarterly taxes, estimated tax payment, how much to set aside for taxes, 1099s, 1099-NEC, year-end tax prep, contractor payments, W-9s, or any phrase suggesting they are preparing for a tax deadline or handing materials to an accountant. Also trigger proactively when a user asks about net profit or YTD income in a context that suggests they are worried about their tax bill.
tools
Prepares tax-season materials — quarterly estimated tax calculation or year-end 1099 prep — and produces an accountant handoff packet. Accepts optional mode and year arguments.
tools
The front door to the Small Business plugin. Listens to what the owner needs right now — vague or specific — and routes them to the best skill or slash command for the moment. Also serves as a guide: explains what's available, suggests what to try next, and adapts recommendations based on stored business context. Trigger whenever the owner asks "what can you do," "help me with my business," "what should I focus on," "I don't know where to start," or any open-ended business request that doesn't clearly match a single skill.