{"id":19450182,"url":"https://github.com/webexsamples/web-calling-sdk-samples","last_synced_at":"2026-04-18T11:33:23.992Z","repository":{"id":217303209,"uuid":"743520387","full_name":"WebexSamples/web-calling-sdk-samples","owner":"WebexSamples","description":"This repository contains examples of scripts that can be used to quickly integrate the Web Calling SDK","archived":false,"fork":false,"pushed_at":"2024-05-07T17:49:06.000Z","size":10,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-07T23:41:15.473Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WebexSamples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-15T12:14:45.000Z","updated_at":"2024-05-07T17:49:06.000Z","dependencies_parsed_at":"2024-01-15T16:25:37.201Z","dependency_job_id":"90157b6f-f189-4583-91d7-a581d73efa58","html_url":"https://github.com/WebexSamples/web-calling-sdk-samples","commit_stats":null,"previous_names":["webexsamples/web-calling-sdk-samples"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fweb-calling-sdk-samples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fweb-calling-sdk-samples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fweb-calling-sdk-samples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fweb-calling-sdk-samples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebexSamples","download_url":"https://codeload.github.com/WebexSamples/web-calling-sdk-samples/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240637241,"owners_count":19833029,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T16:35:37.138Z","updated_at":"2026-04-18T11:33:23.984Z","avatar_url":"https://github.com/WebexSamples.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Webex Web Calling SDK Samples\n\n[![JavaScript](https://img.shields.io/badge/JavaScript-ES6+-yellow.svg)](https://developer.mozilla.org/en-US/docs/Web/JavaScript)\n[![Webex SDK](https://img.shields.io/badge/Webex%20SDK-2.59.8+-blue.svg)](https://www.npmjs.com/package/webex)\n[![License: Cisco Sample Code](https://img.shields.io/badge/License-Cisco%20Sample%20Code-orange.svg)](https://developer.cisco.com/site/license/cisco-sample-code-license/)\n[![HTML5](https://img.shields.io/badge/HTML5-Browser-red.svg)](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5)\n\n## Overview\n\nThis repository contains **comprehensive examples** demonstrating how to quickly integrate the **Webex Web Calling SDK** into your web applications. The samples showcase voice calling capabilities including device registration, call management, and audio handling using the latest Webex JavaScript SDK.\n\n### Key Features\n\n✅ **Two Integration Methods**: CDN and NPM package implementations  \n✅ **Voice Calling**: Place and receive voice calls through Webex  \n✅ **Device Registration**: Register and manage calling devices  \n✅ **Audio Management**: Handle local and remote audio streams  \n✅ **Real-time Events**: Listen to call states and events  \n✅ **Ready-to-Use**: Complete working examples with minimal setup\n\n### Use Cases\n\n- **Softphone Applications**: Build web-based calling interfaces\n- **Customer Support Tools**: Integrate calling into support platforms\n- **Unified Communications**: Add voice calling to existing web apps\n- **Contact Center Solutions**: Build browser-based agent tools\n- **VoIP Applications**: Create web-based communication platforms\n\n## Table of Contents\n\n- [Prerequisites](#prerequisites)\n- [Project Structure](#project-structure)\n- [Quick Start](#quick-start)\n- [Implementation Methods](#implementation-methods)\n  - [CDN Implementation](#cdn-implementation)\n  - [NPM Implementation](#npm-implementation)\n- [Configuration](#configuration)\n- [Usage Guide](#usage-guide)\n- [API Reference](#api-reference)\n- [Audio Handling](#audio-handling)\n- [Event Management](#event-management)\n- [Troubleshooting](#troubleshooting)\n- [Best Practices](#best-practices)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Prerequisites\n\n### Required Accounts \u0026 Credentials\n- **[Webex Developer Account](https://developer.webex.com/)** - Sign up for free\n- **Webex Access Token** - Generated from Developer Portal or OAuth flow\n- **Webex Calling License** - Required for voice calling functionality\n\n### Browser Requirements\n- **Modern Web Browser** with WebRTC support:\n  - Chrome 70+ (recommended)\n  - Firefox 65+\n  - Safari 12+\n  - Edge 79+\n\n### Development Environment\n- **Web Server** - For serving files locally (included in NPM sample)\n- **HTTPS Support** - Required for microphone access in production\n\n### Calling Prerequisites\n- **Webex Calling Organization** - Must have Webex Calling enabled\n- **User License** - User must have Webex Calling license assigned\n- **Device Registration** - Calling device must be registered\n\n## Project Structure\n\n```\nweb-calling-sdk-samples/\n├── cdn/                          # CDN-based implementation\n│   ├── index.html               # Complete HTML interface\n│   └── app.js                   # JavaScript implementation\n├── npm/                          # NPM package implementation\n│   ├── package.json             # Dependencies and scripts\n│   ├── webpack.config.js        # Webpack configuration\n│   └── src/\n│       ├── index.html           # HTML interface\n│       └── index.js             # ES6 module implementation\n├── LICENSE                       # Cisco Sample Code License\n└── README.md                     # This documentation\n```\n\n### Architecture Overview\n\n```mermaid\ngraph TB\n    A[Web Application] --\u003e B[Webex Calling SDK]\n    B --\u003e C[Authentication]\n    B --\u003e D[Device Registration]\n    B --\u003e E[Call Management]\n    \n    C --\u003e F[Access Token]\n    D --\u003e G[Line Registration]\n    E --\u003e H[Audio Streams]\n    \n    F --\u003e I[Webex APIs]\n    G --\u003e I\n    H --\u003e J[WebRTC]\n    \n    I --\u003e K[Webex Cloud]\n    J --\u003e L[Browser Media APIs]\n```\n\n## Quick Start\n\n### Choose Your Implementation Method\n\n#### Option 1: CDN (Fastest Setup)\n```bash\n# Navigate to CDN folder\ncd cdn\n\n# Open index.html in a web server\n# For quick testing with Python:\npython3 -m http.server 8000\n# Then visit: http://localhost:8000\n```\n\n#### Option 2: NPM (Production Ready)\n```bash\n# Navigate to NPM folder\ncd npm\n\n# Install dependencies\nnpm install\n\n# Build the application\nnpm run build\n\n# Start the development server\nnpm start\n# Then visit: http://localhost:1234\n```\n\n## Implementation Methods\n\n### CDN Implementation\n\nThe CDN approach loads the Webex Calling SDK directly from a CDN, making it ideal for rapid prototyping and simple integrations.\n\n#### HTML Structure (`cdn/index.html`)\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"UTF-8\"\u003e\n    \u003ctitle\u003e@webex/calling Quickstart\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003c!-- Authentication Section --\u003e\n    \u003csection id=\"authentication\"\u003e\n        \u003ch2\u003e@webex/calling Quickstart\u003c/h2\u003e\n        \u003cfieldset\u003e\n            \u003clegend\u003eCredentials\u003c/legend\u003e\n            \u003cinput id=\"access-token\" placeholder=\"Access Token\" type=\"text\" required\u003e\n            \u003cbutton id=\"init\" onclick=\"init()\"\u003eInitialize Calling\u003c/button\u003e\n            \u003cpre id=\"access-token-status\"\u003eNot initialized\u003c/pre\u003e\n        \u003c/fieldset\u003e\n        \n        \u003c!-- Device Registration --\u003e\n        \u003cfieldset\u003e\n            \u003clegend\u003eAuthorization with Webex Calling\u003c/legend\u003e\n            \u003cbutton id=\"authenticate_device\" onclick=\"authorize()\"\u003eAuthorize device\u003c/button\u003e\n            \u003cpre id=\"registration-status\"\u003eUnregistered\u003c/pre\u003e\n            \u003cbutton id=\"deregister_device\" onclick=\"deregister()\"\u003eDelete Device\u003c/button\u003e\n        \u003c/fieldset\u003e\n    \u003c/section\u003e\n\n    \u003c!-- Call Management --\u003e\n    \u003csection id=\"calls\"\u003e\n        \u003cfieldset\u003e\n            \u003clegend\u003eCalls\u003c/legend\u003e\n            \u003cinput id=\"destination-number\" placeholder=\"Destination Number\" type=\"text\" required\u003e\n            \u003cbutton id=\"call\" onclick=\"makeCall()\"\u003ePlace call\u003c/button\u003e\n            \u003cpre id=\"call-details\"\u003eNo Call\u003c/pre\u003e\n            \u003cbutton id=\"call\" onclick=\"endCall()\"\u003eEnd call\u003c/button\u003e\n        \u003c/fieldset\u003e\n    \u003c/section\u003e\n\n    \u003c!-- Audio Elements --\u003e\n    \u003caudio id=\"local-audio\" muted=\"true\" autoplay\u003e\u003c/audio\u003e\n    \u003caudio id=\"remote-audio\" autoplay\u003e\u003c/audio\u003e\n\n    \u003c!-- SDK Loading --\u003e\n    \u003cscript src=\"https://unpkg.com/webex@2.59.8-next.10/umd/calling.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"app.js\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### JavaScript Implementation (`cdn/app.js`)\n\n```javascript\n/* global Calling */\n\n// DOM element references\nconst accessToken = document.querySelector('#access-token');\nconst authStatusElm = document.querySelector('#access-token-status');\nconst localAudioElem = document.querySelector('#local-audio');\nconst remoteAudioElem = document.querySelector('#remote-audio');\nconst callDetailsElm = document.querySelector('#call-details');\nconst registerStatus = document.querySelector('#registration-status');\n\n// Global variables\nlet calling;\nlet line;\nlet localAudioStream;\nlet call;\n\nasync function init() {\n    // Webex SDK configuration\n    const webexConfig = {\n        config: {\n            logger: { level: 'debug' },\n            meetings: {\n                reconnection: { enabled: true },\n                enableRtx: true,\n            },\n            encryption: {\n                kmsInitialTimeout: 8000,\n                kmsMaxTimeout: 40000,\n                batcherMaxCalls: 30,\n                caroots: null,\n            },\n            dss: {},\n        },\n        credentials: {\n            access_token: accessToken.value,\n        }\n    };\n    \n    // Calling-specific configuration\n    const callingConfig = {\n        clientConfig: {\n            calling: true,\n            contact: true,\n            callHistory: true,\n            callSettings: true,\n            voicemail: true,\n        },\n        callingClientConfig: {\n            logger: { level: 'info' }\n        },\n        logger: { level: 'info' }\n    };\n\n    // Initialize the calling SDK\n    calling = await Calling.init({webexConfig, callingConfig});\n    authStatusElm.innerHTML = 'Initializing...';\n\n    calling.on(\"ready\", () =\u003e {\n        calling.register().then(() =\u003e {\n            callingClient = calling.callingClient;  \n            authStatusElm.innerHTML = 'Ready';\n        });\n    });\n}\n```\n\n### NPM Implementation\n\nThe NPM approach uses the Webex SDK as a dependency, providing better dependency management and build optimization for production applications.\n\n#### Package Configuration (`npm/package.json`)\n\n```json\n{\n    \"name\": \"calling-dummy\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Webex Calling SDK NPM implementation\",\n    \"main\": \"index.js\",\n    \"license\": \"MIT\",\n    \"dependencies\": {\n        \"webex\": \"2.59.8-next.10\",\n        \"http-server\": \"^14.1.1\"\n    },\n    \"devDependencies\": {\n        \"webpack\": \"^5.88.2\",\n        \"webpack-cli\": \"^5.1.4\"\n    },\n    \"scripts\": {\n        \"start\": \"http-server ./src -p 1234\",\n        \"build\": \"webpack\"\n    }\n}\n```\n\n#### Webpack Configuration (`npm/webpack.config.js`)\n\n```javascript\nconst webpack = require(\"webpack\");\nconst path = require(\"path\");\n\nmodule.exports = {\n    mode: \"development\",\n    entry: \"./src/index.js\",\n    output: {\n        filename: \"main.js\",\n        path: path.resolve(__dirname, \"./src/dist\"),\n    },\n    resolve: {\n        fallback: {\n            http: require.resolve(\"stream-http\"),\n            https: require.resolve(\"https-browserify\"),\n            crypto: require.resolve(\"crypto-browserify\"),\n            stream: require.resolve(\"stream-browserify\"),\n            os: require.resolve(\"os-browserify/browser\"),\n            url: require.resolve(\"url\"),\n            assert: require.resolve(\"assert\"),\n            fs: false,\n            querystring: require.resolve(\"querystring-es3\"),\n        },\n    },\n    plugins: [\n        new webpack.ProvidePlugin({\n            process: \"process/browser\",\n        }),\n    ],\n};\n```\n\n#### ES6 Module Implementation (`npm/src/index.js`)\n\n```javascript\nimport Calling from \"webex/calling\";\n\nlet callingClient;\nlet line;\nlet calling;\n\n// Configuration objects\nconst webexConfig = {\n    config: {\n        logger: { level: \"debug\" },\n        meetings: {\n            reconnection: { enabled: true },\n            enableRtx: true,\n        },\n        encryption: {\n            kmsInitialTimeout: 8000,\n            kmsMaxTimeout: 40000,\n            batcherMaxCalls: 30,\n            caroots: null,\n        },\n        dss: {},\n    },\n    credentials: {\n        access_token: \"YOUR_ACCESS_TOKEN_HERE\",\n    },\n};\n\nconst callingConfig = {\n    clientConfig: {\n        calling: true,\n        contact: true,\n        callHistory: true,\n        callSettings: true,\n        voicemail: true,\n    },\n    callingClientConfig: {\n        logger: { level: \"info\" },\n    },\n    logger: { level: \"info\" },\n};\n\n// Initialize calling functionality\nasync function callingInit() {\n    calling = await Calling.init({ webexConfig, callingConfig });\n    \n    calling.on(\"ready\", () =\u003e {\n        calling.register().then(() =\u003e {\n            callingClient = calling.callingClient;\n        });\n    });\n}\n\n// Register calling line\nasync function registerLine() {\n    line = Object.values(callingClient.getLines())[0];\n    \n    line.on(\"registered\", (lineInfo) =\u003e {\n        console.log(\"Line information: \", lineInfo);\n    });\n    \n    line.register();\n}\n\n// Export functions to global scope for HTML access\nwindow.registerLine = registerLine;\nwindow.callingInit = callingInit;\n```\n\n## Configuration\n\n### Required Configuration Parameters\n\n| Parameter | Description | Example |\n|-----------|-------------|---------|\n| `access_token` | Valid Webex access token | `Bearer eyJ0eXAiOiJKV1Q...` |\n| `logger.level` | Logging verbosity | `'debug'`, `'info'`, `'warn'`, `'error'` |\n| `clientConfig.calling` | Enable calling functionality | `true` |\n| `clientConfig.contact` | Enable contact management | `true` |\n| `clientConfig.callHistory` | Enable call history | `true` |\n\n### Advanced Configuration Options\n\n#### WebRTC Configuration\n```javascript\nconst webexConfig = {\n    config: {\n        meetings: {\n            reconnection: { enabled: true },\n            enableRtx: true,                    // Enable retransmissions\n            enableExtmap: true,                 // Enable extension mapping\n        },\n        encryption: {\n            kmsInitialTimeout: 8000,            // Key management timeout\n            kmsMaxTimeout: 40000,               // Maximum KMS timeout\n            batcherMaxCalls: 30,                // Batch call limit\n        }\n    }\n};\n```\n\n#### Audio Configuration\n```javascript\nconst audioConfig = {\n    constraints: {\n        audio: {\n            echoCancellation: true,\n            noiseSuppression: true,\n            autoGainControl: true,\n            sampleRate: 48000,\n            channelCount: 1\n        }\n    }\n};\n```\n\n## Usage Guide\n\n### Step-by-Step Implementation\n\n#### 1. Initialize the SDK\n\n```javascript\n// Set up configuration\nconst webexConfig = { /* configuration */ };\nconst callingConfig = { /* calling config */ };\n\n// Initialize calling\nconst calling = await Calling.init({webexConfig, callingConfig});\n\n// Wait for ready state\ncalling.on(\"ready\", () =\u003e {\n    console.log(\"Calling SDK is ready\");\n});\n```\n\n#### 2. Register Device\n\n```javascript\n// Register the calling client\nawait calling.register();\n\n// Get the first available line\nconst line = Object.values(calling.callingClient.getLines())[0];\n\n// Listen for registration events\nline.on(\"registered\", (lineInfo) =\u003e {\n    console.log(\"Device registered:\", lineInfo);\n});\n\n// Register the line\nline.register();\n```\n\n#### 3. Handle Audio Streams\n\n```javascript\n// Create microphone stream\nconst localAudioStream = await Calling.createMicrophoneStream({audio: true});\n\n// Attach to local audio element\ndocument.querySelector('#local-audio').srcObject = localAudioStream.outputStream;\n```\n\n#### 4. Make a Call\n\n```javascript\n// Create a call\nconst call = line.makeCall({\n    type: 'uri',\n    address: 'user@example.com'  // or phone number\n});\n\n// Handle call events\ncall.on('progress', (correlationId) =\u003e {\n    console.log('Call in progress');\n});\n\ncall.on('established', (correlationId) =\u003e {\n    console.log('Call established');\n});\n\ncall.on('remote_media', (track) =\u003e {\n    // Handle remote audio\n    const remoteAudio = document.querySelector('#remote-audio');\n    remoteAudio.srcObject = new MediaStream([track]);\n});\n\n// Dial the call\nawait call.dial(localAudioStream);\n```\n\n#### 5. End a Call\n\n```javascript\n// End the active call\ncall.end();\n\n// Clean up audio streams\nlocalAudioStream.stop();\n```\n\n## API Reference\n\n### Core Classes and Methods\n\n#### Calling Class\n\n| Method | Description | Parameters | Returns |\n|--------|-------------|------------|---------|\n| `Calling.init()` | Initialize the calling SDK | `{webexConfig, callingConfig}` | `Promise\u003cCalling\u003e` |\n| `calling.register()` | Register the calling client | None | `Promise\u003cvoid\u003e` |\n| `calling.callingClient` | Get the calling client instance | None | `CallingClient` |\n\n#### CallingClient Class\n\n| Method | Description | Parameters | Returns |\n|--------|-------------|------------|---------|\n| `getLines()` | Get available calling lines | None | `Object\u003cLine\u003e` |\n| `getCall(callId)` | Get call by ID | `callId: string` | `Call` |\n| `getActiveCalls()` | Get all active calls | None | `Array\u003cCall\u003e` |\n\n#### Line Class\n\n| Method | Description | Parameters | Returns |\n|--------|-------------|------------|---------|\n| `register()` | Register the line | None | `Promise\u003cvoid\u003e` |\n| `deregister()` | Deregister the line | None | `Promise\u003cvoid\u003e` |\n| `makeCall(options)` | Create a new call | `{type, address}` | `Call` |\n\n#### Call Class\n\n| Method | Description | Parameters | Returns |\n|--------|-------------|------------|---------|\n| `dial(stream)` | Dial the call | `MediaStream` | `Promise\u003cvoid\u003e` |\n| `answer(stream)` | Answer incoming call | `MediaStream` | `Promise\u003cvoid\u003e` |\n| `end()` | End the call | None | `Promise\u003cvoid\u003e` |\n| `hold()` | Put call on hold | None | `Promise\u003cvoid\u003e` |\n| `resume()` | Resume held call | None | `Promise\u003cvoid\u003e` |\n\n### Static Methods\n\n#### Calling.createMicrophoneStream()\n\nCreates a microphone audio stream for calling.\n\n```javascript\nconst stream = await Calling.createMicrophoneStream({\n    audio: {\n        echoCancellation: true,\n        noiseSuppression: true,\n        autoGainControl: true\n    }\n});\n```\n\n**Parameters:**\n- `constraints` (Object): Media constraints for audio capture\n\n**Returns:**\n- `Promise\u003cLocalAudioStream\u003e`: Audio stream object\n\n## Audio Handling\n\n### Audio Stream Management\n\n```mermaid\ngraph LR\n    A[Microphone] --\u003e B[LocalAudioStream]\n    B --\u003e C[Call.dial()]\n    D[Remote Audio] --\u003e E[MediaTrack]\n    E --\u003e F[Audio Element]\n    \n    G[Local Audio Element] --\u003e B\n    G --\u003e H[Muted for Feedback Prevention]\n```\n\n### Local Audio Setup\n\n```javascript\n// Create and configure local audio stream\nasync function setupLocalAudio() {\n    const localStream = await Calling.createMicrophoneStream({\n        audio: {\n            echoCancellation: true,\n            noiseSuppression: true,\n            autoGainControl: true,\n            sampleRate: 48000\n        }\n    });\n    \n    // Attach to audio element (muted to prevent feedback)\n    const localAudio = document.querySelector('#local-audio');\n    localAudio.srcObject = localStream.outputStream;\n    localAudio.muted = true;  // Important: prevent audio feedback\n    \n    return localStream;\n}\n```\n\n### Remote Audio Handling\n\n```javascript\n// Handle incoming remote audio\ncall.on('remote_media', (track) =\u003e {\n    console.log('Received remote media track:', track);\n    \n    // Create media stream from track\n    const remoteStream = new MediaStream([track]);\n    \n    // Attach to remote audio element\n    const remoteAudio = document.querySelector('#remote-audio');\n    remoteAudio.srcObject = remoteStream;\n    remoteAudio.autoplay = true;\n});\n```\n\n### Audio Quality Optimization\n\n```javascript\n// Configure audio constraints for optimal quality\nconst audioConstraints = {\n    audio: {\n        echoCancellation: true,        // Remove echo\n        noiseSuppression: true,        // Reduce background noise\n        autoGainControl: true,         // Automatic volume adjustment\n        sampleRate: 48000,            // High quality sample rate\n        channelCount: 1,              // Mono audio for calls\n        latency: 0.02,                // Low latency (20ms)\n        volume: 1.0                   // Full volume\n    }\n};\n```\n\n## Event Management\n\n### Call Events\n\n```javascript\n// Complete call event handling\ncall.on('caller_id', (callerIdInfo) =\u003e {\n    console.log('Caller ID:', callerIdInfo.callerId);\n    // Update UI with caller information\n});\n\ncall.on('progress', (correlationId) =\u003e {\n    console.log('Call progress:', correlationId);\n    // Show \"calling...\" state\n});\n\ncall.on('connect', (correlationId) =\u003e {\n    console.log('Call connected:', correlationId);\n    // Show \"connected\" state\n});\n\ncall.on('established', (correlationId) =\u003e {\n    console.log('Call established:', correlationId);\n    // Show \"active call\" state\n    // Enable call controls (mute, hold, etc.)\n});\n\ncall.on('disconnect', (correlationId) =\u003e {\n    console.log('Call disconnected:', correlationId);\n    // Clean up UI and audio streams\n    // Reset call state\n});\n\ncall.on('remote_media', (track) =\u003e {\n    console.log('Remote media received:', track);\n    // Handle incoming audio/video\n});\n```\n\n### Line Events\n\n```javascript\n// Line registration events\nline.on('registered', (lineInfo) =\u003e {\n    console.log('Line registered successfully');\n    console.log('Device ID:', lineInfo.mobiusDeviceId);\n    console.log('User ID:', lineInfo.userId);\n    console.log('SIP Address:', lineInfo.sipAddresses[0]);\n});\n\nline.on('unregistered', (reason) =\u003e {\n    console.log('Line unregistered:', reason);\n});\n\nline.on('incoming_call', (call) =\u003e {\n    console.log('Incoming call received:', call);\n    // Handle incoming call UI\n    // Show answer/decline options\n});\n```\n\n### Calling Client Events\n\n```javascript\n// Calling client events\ncalling.on('ready', () =\u003e {\n    console.log('Calling client is ready');\n});\n\ncalling.on('error', (error) =\u003e {\n    console.error('Calling client error:', error);\n});\n```\n\n## Troubleshooting\n\n### Common Issues\n\n#### 1. Authentication Errors\n**Problem**: 401 Unauthorized errors during initialization.\n\n**Solutions**:\n- Verify access token is valid and not expired\n- Ensure token has calling scopes\n- Check that user has Webex Calling license\n\n```javascript\n// Debug authentication\nconsole.log('Access token:', accessToken.substring(0, 20) + '...');\n```\n\n#### 2. Device Registration Failures\n**Problem**: Line registration fails or times out.\n\n**Solutions**:\n- Verify user has Webex Calling license\n- Check network connectivity\n- Ensure organization has Webex Calling enabled\n\n```javascript\n// Debug registration\nline.on('registration_failed', (error) =\u003e {\n    console.error('Registration failed:', error);\n});\n```\n\n#### 3. Audio Issues\n**Problem**: No audio during calls or microphone not working.\n\n**Solutions**:\n- Check browser permissions for microphone access\n- Verify HTTPS is used (required for getUserMedia)\n- Test microphone with other applications\n\n```javascript\n// Test microphone access\nnavigator.mediaDevices.getUserMedia({audio: true})\n    .then(stream =\u003e console.log('Microphone access granted'))\n    .catch(error =\u003e console.error('Microphone access denied:', error));\n```\n\n#### 4. Call Connection Issues\n**Problem**: Calls fail to connect or have poor quality.\n\n**Solutions**:\n- Check network firewall settings\n- Verify WebRTC is supported in browser\n- Test with different browsers\n\n```javascript\n// Debug call states\ncall.on('connect', () =\u003e console.log('Call connected'));\ncall.on('disconnect', () =\u003e console.log('Call disconnected'));\n```\n\n### Debug Configuration\n\n```javascript\n// Enable comprehensive debugging\nconst debugConfig = {\n    config: {\n        logger: {\n            level: 'debug',\n            historyLength: 1000\n        }\n    },\n    callingClientConfig: {\n        logger: {\n            level: 'debug'\n        }\n    }\n};\n```\n\n### Browser Compatibility Testing\n\n```javascript\n// Check WebRTC support\nfunction checkWebRTCSupport() {\n    const isSupported = !!(\n        navigator.getUserMedia ||\n        navigator.webkitGetUserMedia ||\n        navigator.mozGetUserMedia ||\n        navigator.msGetUserMedia ||\n        navigator.mediaDevices?.getUserMedia\n    );\n    \n    console.log('WebRTC supported:', isSupported);\n    return isSupported;\n}\n```\n\n## Best Practices\n\n### Security\n\n1. **Token Management**:\n   ```javascript\n   // ❌ Don't store tokens in localStorage\n   localStorage.setItem('token', accessToken);\n   \n   // ✅ Use secure, httpOnly cookies or server-side storage\n   // Store tokens server-side and use session authentication\n   ```\n\n2. **HTTPS Requirement**:\n   ```javascript\n   // ✅ Always use HTTPS in production\n   if (location.protocol !== 'https:' \u0026\u0026 location.hostname !== 'localhost') {\n       console.warn('HTTPS required for microphone access');\n   }\n   ```\n\n### Performance\n\n1. **Resource Cleanup**:\n   ```javascript\n   // ✅ Clean up resources when done\n   function cleanupCall() {\n       if (localAudioStream) {\n           localAudioStream.stop();\n       }\n       if (call) {\n           call.end();\n       }\n   }\n   \n   // Clean up on page unload\n   window.addEventListener('beforeunload', cleanupCall);\n   ```\n\n2. **Event Listener Management**:\n   ```javascript\n   // ✅ Remove event listeners to prevent memory leaks\n   function removeCallListeners() {\n       call.off('progress');\n       call.off('established');\n       call.off('disconnect');\n   }\n   ```\n\n### User Experience\n\n1. **Loading States**:\n   ```javascript\n   // ✅ Show loading states during initialization\n   function showLoadingState(message) {\n       document.querySelector('#status').textContent = message;\n   }\n   \n   showLoadingState('Initializing calling...');\n   ```\n\n2. **Error Handling**:\n   ```javascript\n   // ✅ Provide user-friendly error messages\n   function handleCallError(error) {\n       const userMessage = getErrorMessage(error);\n       showUserNotification(userMessage, 'error');\n   }\n   ```\n\n### Production Deployment\n\n1. **Environment Configuration**:\n   ```javascript\n   // Use environment-specific configuration\n   const config = {\n       development: {\n           logger: { level: 'debug' },\n           apiUrl: 'https://api-dev.webex.com'\n       },\n       production: {\n           logger: { level: 'error' },\n           apiUrl: 'https://api.webex.com'\n       }\n   };\n   ```\n\n2. **Error Monitoring**:\n   ```javascript\n   // Implement error tracking\n   calling.on('error', (error) =\u003e {\n       // Send to error tracking service\n       errorTracker.captureException(error);\n   });\n   ```\n\n## Contributing\n\nWe welcome contributions to improve these sample implementations!\n\n### How to Contribute\n\n1. **Fork the repository**\n2. **Create a feature branch**:\n   ```bash\n   git checkout -b feature/your-improvement\n   ```\n3. **Test your changes**:\n   - Test both CDN and NPM implementations\n   - Verify functionality across different browsers\n   - Test with various calling scenarios\n4. **Submit a pull request**\n\n### Contribution Guidelines\n\n- **Code Style**: Follow JavaScript ES6+ best practices\n- **Documentation**: Update README for new features\n- **Testing**: Test with real Webex calling environment\n- **Browser Support**: Ensure compatibility with supported browsers\n\n### Development Setup\n\n1. **Clone the repository**:\n   ```bash\n   git clone \u003crepository-url\u003e\n   cd web-calling-sdk-samples\n   ```\n\n2. **Test CDN implementation**:\n   ```bash\n   cd cdn\n   python3 -m http.server 8000\n   ```\n\n3. **Test NPM implementation**:\n   ```bash\n   cd npm\n   npm install\n   npm run build\n   npm start\n   ```\n\n## License\n\nThis project is licensed under the **Cisco Sample Code License**.\n\n### License Summary\n\n- ✅ **Permitted**: Copy, modify, and redistribute for use with Cisco products\n- ❌ **Prohibited**: Use independent of Cisco products or to compete with Cisco\n- ℹ️ **Warranty**: Provided \"as is\" without warranty\n- ℹ️ **Support**: Not supported by Cisco TAC\n\nSee the [LICENSE](LICENSE) file for full license terms.\n\n---\n\n## Additional Resources\n\n### Webex Developer Resources\n- [Webex Developer Portal](https://developer.webex.com/)\n- [Webex JavaScript SDK Documentation](https://webex.github.io/webex-js-sdk/)\n- [Webex Calling API Reference](https://developer.webex.com/docs/api/guides/webex-calling-api)\n- [WebRTC API Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)\n\n### SDK Resources\n- [Webex JS SDK GitHub](https://github.com/webex/webex-js-sdk)\n- [NPM Package: webex](https://www.npmjs.com/package/webex)\n- [SDK Migration Guide](https://developer.webex.com/blog/webex-javascript-sdk-upgrade-guide)\n\n### WebRTC Resources\n- [WebRTC.org](https://webrtc.org/)\n- [MDN WebRTC Guide](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)\n- [WebRTC Samples](https://webrtc.github.io/samples/)\n\n### Community \u0026 Support\n- [Webex Developer Community](https://developer.webex.com/community)\n- [Stack Overflow - Webex](https://stackoverflow.com/questions/tagged/webex)\n- [Webex Developer Support](https://developer.webex.com/support)\n\n---\n\n**Start Building Amazing Calling Experiences!** 🚀📞\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebexsamples%2Fweb-calling-sdk-samples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebexsamples%2Fweb-calling-sdk-samples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebexsamples%2Fweb-calling-sdk-samples/lists"}