A comprehensive demonstration of implementing click-to-call functionality using the Webex JavaScript SDK. This interactive travel booking application showcases how to integrate voice calling capabilities into web applications, featuring guest calling, call management, and real-time audio handling.
This sample demonstrates advanced Webex Calling SDK integration with real-world use cases:
- Click-to-Call Integration - Seamless calling from web applications
- Guest Calling - Enable calls without Webex accounts using JWE tokens
- Service App Authentication - Secure token-based authentication flow
- Call Management - Mute, hold, transfer, and call control features
- Call History - Recent call tracking and management
- Real-time Audio - WebRTC-based audio streaming and controls
- Travel Booking UI - Professional interface demonstrating business context
- Modern Web Browser with WebRTC support (Chrome, Firefox, Safari, Edge)
- Node.js and npm for package management
- Local web server (included via http-server)
- Webex Developer Account - Sign up for free
- Service App configured for Click-to-Call functionality
- Webex Calling License for the organization
Your Service App must be configured with:
- Click-to-Call permissions for guest calling
- Guest token generation capabilities
- Calling API access for authentication
-
Clone the repository:
git clone <repository-url> cd webex-js-sdk-calling-demo
-
Install dependencies:
yarn install
-
Configure authentication in
js/app.js
:// Update these variables with your Service App credentials let service_app_token = 'your_service_app_access_token_here'; // In getJweToken() function, update: const payload = JSON.stringify({ "calledNumber": "your_destination_number", // Call queue/hunt group number "guestName": "Guest User Name" });
-
Start the development server:
yarn start
-
Access the application:
- Navigate to http://127.0.0.1:9000
- Go to "My Trips" to start the click-to-call setup
- Wait for green circle icon next to Harvey's avatar (indicates Webex Calling registration)
- Click "Call Support" to place a call
webex-js-sdk-calling-demo/
βββ index.html # Main landing page
βββ mytrips.html # Travel booking interface with calling
βββ agent.html # Agent dashboard (if applicable)
βββ js/
β βββ app.js # Main application logic and authentication
β βββ timer.js # Call timer functionality
β βββ *.js # Additional JavaScript utilities
βββ sdk/
β βββ calling-sdk.js # Webex Calling SDK integration
β βββ demo.js # Demo-specific functionality
βββ controls/ # Call control components
β βββ controls.html # Call control interface
β βββ controls.css # Call control styling
β βββ controls.js # Call control logic
βββ css/ # Styling and themes
βββ images/ # Application assets
βββ fonts/ # Typography resources
βββ package.json # Dependencies and scripts
βββ README.md # This file
Feature | Description | Implementation |
---|---|---|
Guest Authentication | Generate guest tokens for calling | getGuestToken() function |
JWE Token Generation | Create click-to-call tokens | getJweToken() function |
Call Initiation | Start calls from web interface | initiateCall() function |
Call Controls | Mute, hold, transfer operations | Call control buttons |
Call History | View recent call records | renderCallHistory() function |
Real-time Audio | WebRTC audio stream handling | Audio element management |
sequenceDiagram
participant App as Web App
participant SA as Service App
participant Webex as Webex APIs
participant Guest as Guest User
App->>SA: Request Guest Token
SA->>Webex: POST /v1/guests/token
Webex->>SA: Guest Access Token
SA->>App: Return Guest Token
App->>SA: Request JWE Token
SA->>Webex: POST /v1/telephony/click2call/callToken
Webex->>SA: JWE Call Token
SA->>App: Return JWE Token
App->>Webex: Initialize Calling SDK
Guest->>App: Click "Call Support"
App->>Webex: Initiate Call
async function getGuestToken() {
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", `Bearer ${service_app_token}`);
const raw = JSON.stringify({
"subject": "Webex Click To Call Demo",
"displayName": "WebexOne Demo"
});
const response = await fetch("https://webexapis.com/v1/guests/token", {
method: "POST",
headers: myHeaders,
body: raw
});
const data = await response.json();
return data.accessToken;
}
async function getJweToken() {
const myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${service_app_token}`);
const payload = JSON.stringify({
"calledNumber": "your_destination_number",
"guestName": "Guest User"
});
const response = await fetch("https://webexapis.com/v1/telephony/click2call/callToken", {
method: "POST",
headers: myHeaders,
body: payload
});
const result = await response.json();
return result.callToken;
}
async function getWebexConfig(userType) {
const guestToken = await getGuestToken();
return {
config: {
logger: { level: "debug" },
meetings: {
reconnection: { enabled: true },
enableRtx: true
},
encryption: {
kmsInitialTimeout: 8000,
kmsMaxTimeout: 40000,
batcherMaxCalls: 30
}
},
credentials: {
access_token: guestToken
}
};
}
async function initCalling(userType) {
const webexConfig = await getWebexConfig(userType);
const callingConfig = await getCallingConfig();
// Initialize Calling SDK
calling = await Calling.init({ webexConfig, callingConfig });
calling.on("ready", () => {
calling.register().then(async () => {
callingClient = calling.callingClient;
line = Object.values(callingClient?.getLines())[0];
setupLineListeners();
line.register();
});
});
}
function setupLineListeners() {
line.on('registered', (lineInfo) => {
line = lineInfo;
updateAvailability(); // Show green circle indicator
});
// Handle incoming calls
line.on('line:incoming_call', (callObj) => {
openCallNotification(callObj);
incomingCall = callObj;
});
}
// Initiate outbound call
async function initiateCall() {
try {
const destination = "destination_number";
call = line.makeCall({
type: 'uri',
address: destination
});
setupCallListeners(call);
await call.dial();
} catch (error) {
console.error('Call initiation failed:', error);
}
}
// Call control functions
function toggleMute() {
if (call) {
call.isMuted() ? call.unmute() : call.mute();
callNotification.muteToggle();
}
}
function toggleHold() {
if (call) {
call.isHeld() ? call.resume() : call.hold();
callNotification.holdToggle();
}
}
The application presents a realistic travel booking scenario:
- Homepage: Travel destination showcase with navigation
- My Trips: Booking management interface with call support
- Call Controls: In-call management overlay
- Call History: Recent call tracking and management
<div class="incoming-call-notification timestate" id="callNotification">
<div class="notifier-a">
<div class="call-avatar">C</div>
<div class="call-info">
<div class="callee-name">Customer Care</div>
</div>
<div class="call-time" id="call-time">
<span id="timer">00:01</span>
<span class="blink_me" id="hold-status">On Hold</span>
</div>
<div class="call-actions">
<button class="cut-call-btn" onclick="disconnectCall()">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="notifier-a-controls">
<button class="mute tooltip-trigger" data-tooltip="Mute" onclick="toggleMute()">
<i class="fa fa-microphone-slash"></i>
</button>
</div>
</div>
- Mute/Unmute: Toggle microphone state
- Hold/Resume: Put calls on hold
- Call Timer: Track call duration
- Visual Indicators: Connection status and availability
- Tooltips: User-friendly control descriptions
Setting | Description | Example |
---|---|---|
service_app_token |
Service App access token | Bearer eyJ0eXAiOiJKV1Q... |
calledNumber |
Destination number for calls | +1234567890 |
guestName |
Display name for guest users | "Customer Support Guest" |
async function getCallingConfig() {
const jweToken = await getJweToken();
return {
clientConfig: {
calling: true,
callHistory: true
},
callingClientConfig: {
logger: { level: "info" },
discovery: {
region: "US-EAST",
country: "US"
},
serviceData: {
indicator: 'guestcalling',
domain: '',
guestName: 'Harvey'
},
jwe: jweToken
},
logger: { level: "info" }
};
}
-
Token Management: Store service app tokens securely
// Use environment variables or secure storage const service_app_token = process.env.WEBEX_SERVICE_APP_TOKEN;
-
HTTPS Requirement: Ensure HTTPS for WebRTC functionality
-
Input Validation: Validate all user inputs and phone numbers
-
Rate Limiting: Implement calling rate limits
// Lazy load SDK components
const loadCallingSDK = async () => {
if (!window.Calling) {
await import('https://unpkg.com/[email protected]/umd/calling.min.js');
}
return window.Calling;
};
async function initCalling(userType) {
try {
calling = await Calling.init({ webexConfig, callingConfig });
} catch (error) {
console.error('Calling initialization failed:', error);
// Implement user-friendly error messaging
showErrorMessage('Unable to initialize calling. Please try again.');
}
}
-
Install dependencies:
yarn install
-
Start development server:
yarn start
-
Development with debugging:
// Enable debug logging const webexConfig = { config: { logger: { level: "debug" } } };
- Verify Service App token: Ensure valid access token
- Check call destination: Confirm destination number is reachable
- Test browser compatibility: Verify WebRTC support
- Monitor console logs: Check for SDK initialization errors
- Test call controls: Verify mute, hold, and disconnect functions
Example: Adding video calling support:
// Enable video in calling config
const callingConfig = {
clientConfig: {
calling: true,
video: true, // Enable video calling
callHistory: true
}
};
// Handle video streams
call.on('media:local_video', (stream) => {
document.getElementById('local-video').srcObject = stream;
});
call.on('media:remote_video', (stream) => {
document.getElementById('remote-video').srcObject = stream;
});
- Webex Web Calling SDK Introduction
- Core Calling Concepts
- Calling Quickstart Guide
- Authorization Guide
- Background Noise Removal
- Supplementary Services
- Voicemail Integration
- Contacts Management
- Call History
- Call Settings
- Webex SDK Contributors: https://eurl.io/#v-LbYXL27
- Chrome Extension Support: https://eurl.io/#YbnG_BwcN
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly with real calling scenarios
- Submit a pull request
This project is licensed under the Cisco Sample Code License.
- β Permitted: Copy, modify, and redistribute for use with Cisco products
- β Prohibited: Use independent of Cisco products or to compete with Cisco
- βΉοΈ Warranty: Provided "as is" without warranty
- βΉοΈ Support: Not supported by Cisco TAC
See the LICENSE file for full license terms.
Ready to integrate click-to-call with Webex! ππ