Cisco Webex

CommunicationEnterpriseVideo ConferenceAPIBot DevelopmentSDKWebhook

Communication Tool

Cisco Webex

Overview

Cisco Webex is an enterprise-grade comprehensive collaboration platform. It integrates high-quality video conferencing, team collaboration features, and enterprise-level security, providing large-scale meeting capabilities supporting up to 100,000 participants. With Webex SDK v3, powerful bot development features, and rich webhook integration, it enables the construction of customized enterprise solutions.

Details

Cisco Webex evolved from WebEx, founded in 1995 and later acquired by Cisco, developing into the current Webex platform. It is widely adopted by large enterprises, government agencies, and educational institutions worldwide, particularly known for its high-quality video conferencing and security features.

In 2024-2025, major implementations included large-scale migration to Webex SDK v3, AI Transcription API, Webex Assistant API, Real-time Media API, Advanced Spaces API, new Webhook Events, and significant expansion of Contact Center API. Additionally, Zero Trust Security architecture enhancement, Cloud-Native Infrastructure migration, and WebRTC optimization were implemented, greatly improving stability and performance in enterprise environments.

Webex API provides JavaScript SDK, Python SDK, .NET SDK, REST API, and GraphQL API, enabling bot development, custom application development, and system integration. Integration with Adaptive Cards UI and Webex Devices also provides consistent experiences from meeting room systems to mobile devices.

Pros and Cons

Pros

  • Enterprise Security: Zero Trust Security, end-to-end encryption
  • Large-scale Meeting Support: Up to 100,000 participants, webinar and broadcast functionality
  • Rich APIs/SDKs: JavaScript, Python, .NET SDK, REST API, GraphQL
  • Hybrid Environment Support: Hybrid deployment of on-premises and cloud
  • AI Feature Integration: Real-time transcription, Webex Assistant, noise removal
  • Device Integration: Complete integration with Webex Devices and meeting room systems
  • Compliance: HIPAA, FedRAMP, SOC2 certifications
  • Global Deployment: Multi-region data centers, low latency

Cons

  • High Cost: Enterprise pricing structure, complex licensing system
  • Complexity: Configuration and management complexity due to rich features
  • Learning Curve: Acquisition costs for administrators and developers
  • Cisco Dependency: Dependence on Cisco ecosystem
  • Not for Small Enterprises: Excessive for startups and small teams
  • Customization Limitations: UI/UX customization constraints

Key Links

Code Examples

Bot Development with Webex JavaScript SDK v3

// Webex JavaScript SDK v3 Bot implementation
import { Webex } from '@webex/sdk';

class WebexBot {
    constructor(accessToken) {
        this.webex = Webex.init({
            credentials: {
                access_token: accessToken
            },
            config: {
                logger: {
                    level: 'info'
                },
                meetings: {
                    enableExperimentalSupport: true
                }
            }
        });
        
        this.setupEventHandlers();
    }

    setupEventHandlers() {
        // New message events
        this.webex.messages.on('created', (message) => {
            this.handleMessage(message);
        });

        // Mention notifications
        this.webex.messages.on('created', (message) => {
            if (message.mentionedPeople && message.mentionedPeople.includes(this.webex.people.me.id)) {
                this.handleMention(message);
            }
        });

        // Meeting events
        this.webex.meetings.on('meeting:added', (meeting) => {
            console.log('🎥 New meeting created:', meeting.id);
        });

        // Space creation events
        this.webex.rooms.on('created', (room) => {
            console.log('🏠 New Space created:', room.title);
        });
    }

    async handleMessage(message) {
        // Ignore own messages
        if (message.personId === this.webex.people.me.id) return;

        console.log(`📩 Message received: ${message.text}`);

        // Command processing
        if (message.text && message.text.startsWith('/')) {
            await this.processCommand(message);
        }

        // Keyword detection
        if (message.text && message.text.toLowerCase().includes('help')) {
            await this.sendHelpMessage(message.roomId);
        }

        // Meeting schedule request
        if (message.text && message.text.toLowerCase().includes('schedule meeting')) {
            await this.scheduleMeeting(message);
        }
    }

    async processCommand(message) {
        const [command, ...args] = message.text.slice(1).split(' ');
        
        switch (command.toLowerCase()) {
            case 'ping':
                await this.sendMessage(message.roomId, '🏓 Pong!');
                break;
            case 'status':
                await this.sendSystemStatus(message.roomId);
                break;
            case 'weather':
                await this.sendWeatherInfo(message.roomId, args[0] || 'Tokyo');
                break;
            case 'meeting':
                await this.handleMeetingCommand(message, args);
                break;
            case 'space':
                await this.handleSpaceCommand(message, args);
                break;
            case 'people':
                await this.listSpaceMembers(message.roomId);
                break;
            default:
                await this.sendMessage(message.roomId, `❓ Unknown command: ${command}. Type /help for help.`);
        }
    }

    async sendMessage(roomId, text, attachments = null) {
        try {
            const messageData = {
                roomId: roomId,
                text: text
            };

            if (attachments) {
                messageData.attachments = attachments;
            }

            const response = await this.webex.messages.create(messageData);
            return response;
        } catch (error) {
            console.error('Message send error:', error);
        }
    }

    async sendAdaptiveCard(roomId, cardData) {
        try {
            const attachments = [{
                contentType: 'application/vnd.microsoft.card.adaptive',
                content: cardData
            }];

            await this.sendMessage(roomId, 'Card-based message:', attachments);
        } catch (error) {
            console.error('Adaptive Card send error:', error);
        }
    }

    async sendHelpMessage(roomId) {
        const helpCard = {
            type: 'AdaptiveCard',
            version: '1.4',
            body: [
                {
                    type: 'TextBlock',
                    text: '🤖 Webex Bot Commands',
                    weight: 'Bolder',
                    size: 'Medium'
                },
                {
                    type: 'TextBlock',
                    text: 'Available commands:'
                },
                {
                    type: 'FactSet',
                    facts: [
                        { title: '/ping', value: 'Connection test' },
                        { title: '/status', value: 'System status display' },
                        { title: '/weather [city]', value: 'Weather information' },
                        { title: '/meeting create', value: 'Create meeting' },
                        { title: '/space info', value: 'Space information' },
                        { title: '/people', value: 'Member list' }
                    ]
                }
            ],
            actions: [
                {
                    type: 'Action.Submit',
                    title: 'Check System Status',
                    data: {
                        action: 'status'
                    }
                }
            ]
        };

        await this.sendAdaptiveCard(roomId, helpCard);
    }

    async sendSystemStatus(roomId) {
        const os = require('os');
        const uptimeHours = Math.floor(os.uptime() / 3600);
        const uptimeMinutes = Math.floor((os.uptime() % 3600) / 60);
        const memoryUsage = Math.round((os.totalmem() - os.freemem()) / os.totalmem() * 100);

        const statusCard = {
            type: 'AdaptiveCard',
            version: '1.4',
            body: [
                {
                    type: 'TextBlock',
                    text: '📊 System Status',
                    weight: 'Bolder',
                    size: 'Medium'
                },
                {
                    type: 'FactSet',
                    facts: [
                        { title: 'Uptime', value: `${uptimeHours}h ${uptimeMinutes}m` },
                        { title: 'Memory Usage', value: `${memoryUsage}%` },
                        { title: 'Platform', value: `${os.platform()} ${os.arch()}` },
                        { title: 'Node.js', value: process.version },
                        { title: 'Webex SDK', value: 'Connected ✅' }
                    ]
                }
            ]
        };

        await this.sendAdaptiveCard(roomId, statusCard);
    }

    async sendWeatherInfo(roomId, city) {
        try {
            const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}&units=metric`);
            const weather = await response.json();

            if (weather.cod === 200) {
                const weatherCard = {
                    type: 'AdaptiveCard',
                    version: '1.4',
                    body: [
                        {
                            type: 'TextBlock',
                            text: `🌤️ Weather in ${weather.name}`,
                            weight: 'Bolder',
                            size: 'Medium'
                        },
                        {
                            type: 'FactSet',
                            facts: [
                                { title: 'Weather', value: weather.weather[0].description },
                                { title: 'Temperature', value: `${weather.main.temp}°C` },
                                { title: 'Humidity', value: `${weather.main.humidity}%` },
                                { title: 'Wind Speed', value: `${weather.wind.speed} m/s` }
                            ]
                        }
                    ]
                };

                await this.sendAdaptiveCard(roomId, weatherCard);
            } else {
                await this.sendMessage(roomId, '❌ Failed to fetch weather information');
            }
        } catch (error) {
            console.error('Weather API error:', error);
            await this.sendMessage(roomId, '❌ Error occurred while fetching weather information');
        }
    }

    async handleMeetingCommand(message, args) {
        const subCommand = args[0]?.toLowerCase();

        switch (subCommand) {
            case 'create':
                await this.createMeeting(message.roomId, args.slice(1));
                break;
            case 'list':
                await this.listMeetings(message.roomId);
                break;
            case 'join':
                await this.joinMeeting(message.roomId, args[1]);
                break;
            default:
                await this.sendMessage(message.roomId, 'Available meeting commands: create, list, join');
        }
    }

    async createMeeting(roomId, args) {
        try {
            const title = args.join(' ') || 'Bot Created Meeting';
            const startTime = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes later
            const endTime = new Date(startTime.getTime() + 60 * 60 * 1000); // 1 hour

            const meetingData = {
                title: title,
                start: startTime.toISOString(),
                end: endTime.toISOString(),
                timezone: 'UTC',
                enabledAutoRecordMeeting: false,
                allowAnyUserToBeCoHost: true
            };

            const meeting = await this.webex.meetings.create(meetingData);

            const meetingCard = {
                type: 'AdaptiveCard',
                version: '1.4',
                body: [
                    {
                        type: 'TextBlock',
                        text: '🎥 Meeting Created',
                        weight: 'Bolder',
                        size: 'Medium'
                    },
                    {
                        type: 'FactSet',
                        facts: [
                            { title: 'Title', value: meeting.title },
                            { title: 'Start Time', value: new Date(meeting.start).toLocaleString() },
                            { title: 'Meeting ID', value: meeting.meetingNumber },
                            { title: 'Web Link', value: meeting.webLink }
                        ]
                    }
                ],
                actions: [
                    {
                        type: 'Action.OpenUrl',
                        title: 'Join Meeting',
                        url: meeting.webLink
                    }
                ]
            };

            await this.sendAdaptiveCard(roomId, meetingCard);
        } catch (error) {
            console.error('Meeting creation error:', error);
            await this.sendMessage(roomId, '❌ Failed to create meeting');
        }
    }

    async listMeetings(roomId) {
        try {
            const meetings = await this.webex.meetings.list({
                from: new Date().toISOString(),
                to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // Next week
            });

            if (meetings.items && meetings.items.length > 0) {
                const meetingList = meetings.items.slice(0, 5).map(meeting => ({
                    title: meeting.title,
                    value: `${new Date(meeting.start).toLocaleString()} - ${meeting.meetingNumber}`
                }));

                const listCard = {
                    type: 'AdaptiveCard',
                    version: '1.4',
                    body: [
                        {
                            type: 'TextBlock',
                            text: '📅 Upcoming Meetings',
                            weight: 'Bolder',
                            size: 'Medium'
                        },
                        {
                            type: 'FactSet',
                            facts: meetingList
                        }
                    ]
                };

                await this.sendAdaptiveCard(roomId, listCard);
            } else {
                await this.sendMessage(roomId, '📅 No upcoming meetings');
            }
        } catch (error) {
            console.error('Meeting list error:', error);
            await this.sendMessage(roomId, '❌ Failed to list meetings');
        }
    }

    async handleSpaceCommand(message, args) {
        const subCommand = args[0]?.toLowerCase();

        switch (subCommand) {
            case 'info':
                await this.getSpaceInfo(message.roomId);
                break;
            case 'create':
                await this.createSpace(message.roomId, args.slice(1));
                break;
            default:
                await this.sendMessage(message.roomId, 'Available space commands: info, create');
        }
    }

    async getSpaceInfo(roomId) {
        try {
            const room = await this.webex.rooms.get(roomId);
            
            const spaceCard = {
                type: 'AdaptiveCard',
                version: '1.4',
                body: [
                    {
                        type: 'TextBlock',
                        text: '🏠 Space Information',
                        weight: 'Bolder',
                        size: 'Medium'
                    },
                    {
                        type: 'FactSet',
                        facts: [
                            { title: 'Title', value: room.title },
                            { title: 'Type', value: room.type === 'group' ? 'Group' : 'Direct' },
                            { title: 'Created', value: new Date(room.created).toLocaleDateString() },
                            { title: 'Space ID', value: room.id }
                        ]
                    }
                ]
            };

            await this.sendAdaptiveCard(roomId, spaceCard);
        } catch (error) {
            console.error('Space info error:', error);
            await this.sendMessage(roomId, '❌ Failed to get space information');
        }
    }

    async listSpaceMembers(roomId) {
        try {
            const memberships = await this.webex.memberships.list({ roomId: roomId });
            
            if (memberships.items && memberships.items.length > 0) {
                let memberList = [];
                
                for (const membership of memberships.items.slice(0, 10)) {
                    const person = await this.webex.people.get(membership.personId);
                    memberList.push({
                        title: person.displayName,
                        value: person.emails[0]
                    });
                }

                const membersCard = {
                    type: 'AdaptiveCard',
                    version: '1.4',
                    body: [
                        {
                            type: 'TextBlock',
                            text: `👥 Member List (${memberships.items.length})`,
                            weight: 'Bolder',
                            size: 'Medium'
                        },
                        {
                            type: 'FactSet',
                            facts: memberList
                        }
                    ]
                };

                await this.sendAdaptiveCard(roomId, membersCard);
            }
        } catch (error) {
            console.error('Member list error:', error);
            await this.sendMessage(roomId, '❌ Failed to list members');
        }
    }

    async uploadFile(roomId, filePath, fileName) {
        const fs = require('fs');
        
        try {
            const fileBuffer = fs.readFileSync(filePath);
            
            const fileData = {
                roomId: roomId,
                files: [fileBuffer],
                filename: fileName
            };

            const response = await this.webex.messages.create(fileData);
            console.log(`📎 File upload complete: ${fileName}`);
            return response;
        } catch (error) {
            console.error('File upload error:', error);
        }
    }

    async start() {
        try {
            await this.webex.once('ready');
            console.log('🚀 Webex Bot started');
            
            // Get own information
            this.webex.people.me = await this.webex.people.get('me');
            console.log(`Bot info: ${this.webex.people.me.displayName} (${this.webex.people.me.emails[0]})`);
            
            // Register webhooks (for production)
            if (process.env.WEBHOOK_URL) {
                await this.registerWebhooks();
            }
            
            // Setup periodic tasks
            this.setupPeriodicTasks();
        } catch (error) {
            console.error('Bot start error:', error);
        }
    }

    async registerWebhooks() {
        try {
            // Message webhook
            await this.webex.webhooks.create({
                name: 'Bot Messages',
                targetUrl: process.env.WEBHOOK_URL + '/messages',
                resource: 'messages',
                event: 'created'
            });

            // Meeting webhook
            await this.webex.webhooks.create({
                name: 'Bot Meetings',
                targetUrl: process.env.WEBHOOK_URL + '/meetings',
                resource: 'meetings',
                event: 'created'
            });

            console.log('✅ Webhook registration complete');
        } catch (error) {
            console.error('Webhook registration error:', error);
        }
    }

    setupPeriodicTasks() {
        // Hourly health check
        setInterval(async () => {
            console.log('⏰ Running periodic health check...');
        }, 3600000);

        // Daily report (every day at 9 AM)
        const now = new Date();
        const msUntil9AM = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 9, 0, 0, 0) - now;
        
        setTimeout(() => {
            setInterval(async () => {
                console.log('📊 Generating daily report...');
                // Send report to admin space (implementation example)
            }, 24 * 60 * 60 * 1000);
        }, msUntil9AM);
    }

    async stop() {
        console.log('🛑 Stopping Webex Bot');
        // Cleanup process
    }
}

// Usage example
async function main() {
    const accessToken = process.env.WEBEX_ACCESS_TOKEN;
    
    if (!accessToken) {
        console.error('❌ WEBEX_ACCESS_TOKEN not set');
        process.exit(1);
    }

    const bot = new WebexBot(accessToken);

    // Graceful shutdown
    process.on('SIGINT', async () => {
        console.log('🔄 Shutting down...');
        await bot.stop();
        process.exit(0);
    });

    await bot.start();
}

main().catch(console.error);

Webex Webhook Server Implementation

// Express.js Webhook receiver server
const express = require('express');
const crypto = require('crypto');
const { Webex } = require('@webex/sdk');

const app = express();
app.use(express.json());

class WebexWebhookServer {
    constructor(accessToken, webhookSecret) {
        this.webex = Webex.init({
            credentials: {
                access_token: accessToken
            }
        });
        this.webhookSecret = webhookSecret;
    }

    // Webhook signature verification
    verifyWebhookSignature(req, res, next) {
        const signature = req.headers['x-spark-signature'];
        const body = JSON.stringify(req.body);
        
        if (this.webhookSecret) {
            const expectedSignature = crypto
                .createHmac('sha1', this.webhookSecret)
                .update(body)
                .digest('hex');
            
            if (signature !== expectedSignature) {
                return res.status(401).send('Invalid signature');
            }
        }
        
        next();
    }

    // Message webhook processing
    async handleMessageWebhook(req, res) {
        const webhookData = req.body;
        
        try {
            // Process only new messages
            if (webhookData.event === 'created') {
                const message = await this.webex.messages.get(webhookData.data.id);
                
                // Ignore bot's own messages
                if (message.personId === (await this.webex.people.get('me')).id) {
                    return res.status(200).send('OK');
                }
                
                console.log(`📩 Webhook received: ${message.text}`);
                
                // Execute bot processing
                await this.processMessage(message);
            }
            
            res.status(200).send('OK');
        } catch (error) {
            console.error('Webhook processing error:', error);
            res.status(500).send('Server error');
        }
    }

    // Meeting webhook processing
    async handleMeetingWebhook(req, res) {
        const webhookData = req.body;
        
        try {
            console.log('🎥 Meeting webhook received:', webhookData);
            
            // Meeting creation notification
            if (webhookData.event === 'created') {
                const meeting = webhookData.data;
                await this.notifyMeetingCreated(meeting);
            }
            
            res.status(200).send('OK');
        } catch (error) {
            console.error('Meeting webhook processing error:', error);
            res.status(500).send('Server error');
        }
    }

    async processMessage(message) {
        // CI/CD notification processing
        if (message.text && message.text.includes('deployment')) {
            await this.handleDeploymentNotification(message);
        }
        
        // GitHub integration
        if (message.text && message.text.includes('github')) {
            await this.handleGitHubIntegration(message);
        }
        
        // Alert processing
        if (message.text && message.text.toLowerCase().includes('alert')) {
            await this.handleAlertMessage(message);
        }
    }

    async handleDeploymentNotification(message) {
        // Notify deployment status to Webex
        const deployCard = {
            type: 'AdaptiveCard',
            version: '1.4',
            body: [
                {
                    type: 'TextBlock',
                    text: '🚀 Deployment Notification',
                    weight: 'Bolder',
                    size: 'Medium'
                },
                {
                    type: 'FactSet',
                    facts: [
                        { title: 'Status', value: 'In Progress' },
                        { title: 'Environment', value: 'Production' },
                        { title: 'Start Time', value: new Date().toLocaleString() }
                    ]
                }
            ],
            actions: [
                {
                    type: 'Action.OpenUrl',
                    title: 'View Logs',
                    url: 'https://your-ci-cd-platform.com/logs'
                }
            ]
        };

        await this.sendAdaptiveCard(message.roomId, deployCard);
    }

    async sendAdaptiveCard(roomId, cardData) {
        const attachments = [{
            contentType: 'application/vnd.microsoft.card.adaptive',
            content: cardData
        }];

        await this.webex.messages.create({
            roomId: roomId,
            text: 'Card-based message:',
            attachments: attachments
        });
    }

    // System monitoring integration
    async sendSystemAlert(roomId, alertData) {
        const severity = alertData.severity || 'warning';
        const colors = {
            'critical': 'attention',
            'warning': 'warning',
            'info': 'good'
        };

        const alertCard = {
            type: 'AdaptiveCard',
            version: '1.4',
            body: [
                {
                    type: 'TextBlock',
                    text: `🚨 System Alert: ${alertData.title}`,
                    weight: 'Bolder',
                    size: 'Medium',
                    color: colors[severity]
                },
                {
                    type: 'TextBlock',
                    text: alertData.description,
                    wrap: true
                },
                {
                    type: 'FactSet',
                    facts: [
                        { title: 'Severity', value: severity.toUpperCase() },
                        { title: 'Service', value: alertData.service },
                        { title: 'Timestamp', value: new Date(alertData.timestamp).toLocaleString() }
                    ]
                }
            ],
            actions: [
                {
                    type: 'Action.OpenUrl',
                    title: 'View Dashboard',
                    url: alertData.dashboardUrl || 'https://your-monitoring-dashboard.com'
                }
            ]
        };

        await this.sendAdaptiveCard(roomId, alertCard);
    }

    startServer(port = 3000) {
        // Webhook endpoint setup
        app.post('/webhooks/messages', 
            this.verifyWebhookSignature.bind(this),
            this.handleMessageWebhook.bind(this)
        );

        app.post('/webhooks/meetings',
            this.verifyWebhookSignature.bind(this),
            this.handleMeetingWebhook.bind(this)
        );

        // External system integration endpoints
        app.post('/api/alert', async (req, res) => {
            const { roomId, alertData } = req.body;
            
            try {
                await this.sendSystemAlert(roomId, alertData);
                res.status(200).json({ success: true });
            } catch (error) {
                console.error('Alert send error:', error);
                res.status(500).json({ error: 'Failed to send alert' });
            }
        });

        // GitHub Webhook
        app.post('/api/github', async (req, res) => {
            const githubEvent = req.headers['x-github-event'];
            const payload = req.body;
            
            try {
                await this.handleGitHubEvent(githubEvent, payload);
                res.status(200).send('OK');
            } catch (error) {
                console.error('GitHub webhook processing error:', error);
                res.status(500).send('Error');
            }
        });

        // Health check
        app.get('/health', (req, res) => {
            res.status(200).json({ 
                status: 'healthy',
                timestamp: new Date().toISOString()
            });
        });

        app.listen(port, () => {
            console.log(`🚀 Webex Webhook server started on port ${port}`);
        });
    }

    async handleGitHubEvent(eventType, payload) {
        const roomId = process.env.GITHUB_NOTIFICATION_ROOM;
        if (!roomId) return;

        switch (eventType) {
            case 'push':
                await this.handleGitHubPush(roomId, payload);
                break;
            case 'pull_request':
                await this.handleGitHubPR(roomId, payload);
                break;
            case 'issues':
                await this.handleGitHubIssue(roomId, payload);
                break;
        }
    }

    async handleGitHubPush(roomId, payload) {
        const pushCard = {
            type: 'AdaptiveCard',
            version: '1.4',
            body: [
                {
                    type: 'TextBlock',
                    text: `🚀 Push to ${payload.repository.full_name}`,
                    weight: 'Bolder',
                    size: 'Medium'
                },
                {
                    type: 'FactSet',
                    facts: [
                        { title: 'Commits', value: payload.commits.length.toString() },
                        { title: 'Branch', value: payload.ref.split('/').pop() },
                        { title: 'Pusher', value: payload.pusher.name }
                    ]
                },
                {
                    type: 'TextBlock',
                    text: '**Latest commit:**',
                    weight: 'Bolder'
                }
            ]
        };

        // Add latest commit details
        if (payload.commits.length > 0) {
            const latestCommit = payload.commits[0];
            pushCard.body.push({
                type: 'FactSet',
                facts: [
                    { title: 'Message', value: latestCommit.message },
                    { title: 'Commit ID', value: latestCommit.id.substring(0, 7) },
                    { title: 'Author', value: latestCommit.author.name }
                ]
            });
        }

        pushCard.actions = [
            {
                type: 'Action.OpenUrl',
                title: 'View Changes',
                url: payload.compare
            }
        ];

        await this.sendAdaptiveCard(roomId, pushCard);
    }
}

// Usage example
async function startWebhookServer() {
    const accessToken = process.env.WEBEX_ACCESS_TOKEN;
    const webhookSecret = process.env.WEBEX_WEBHOOK_SECRET;
    
    if (!accessToken) {
        console.error('❌ WEBEX_ACCESS_TOKEN not set');
        process.exit(1);
    }

    const webhookServer = new WebexWebhookServer(accessToken, webhookSecret);
    webhookServer.startServer(process.env.PORT || 3000);
}

startWebhookServer().catch(console.error);

Python SDK Bot Development

# Python Webex Bot implementation using webexteamssdk
import os
import asyncio
from webexteamssdk import WebexTeamsAPI
from flask import Flask, request, jsonify
import threading

class WebexPythonBot:
    def __init__(self, access_token):
        self.api = WebexTeamsAPI(access_token=access_token)
        self.bot_id = self.api.people.me().id
        
    def handle_message(self, message_data):
        """Message processing"""
        message = self.api.messages.get(message_data['id'])
        
        # Ignore bot's own messages
        if message.personId == self.bot_id:
            return
        
        print(f"📩 Message received: {message.text}")
        
        # Command processing
        if message.text and message.text.startswith('/'):
            self.process_command(message)
        
        # Keyword detection
        elif message.text and 'help' in message.text.lower():
            self.send_help_message(message.roomId)
    
    def process_command(self, message):
        """Command processing"""
        parts = message.text[1:].split()
        command = parts[0].lower()
        args = parts[1:] if len(parts) > 1 else []
        
        if command == 'ping':
            self.send_message(message.roomId, '🏓 Pong!')
        
        elif command == 'status':
            self.send_system_status(message.roomId)
        
        elif command == 'weather':
            city = args[0] if args else 'Tokyo'
            self.send_weather_info(message.roomId, city)
        
        elif command == 'meeting':
            if args and args[0] == 'create':
                self.create_meeting(message.roomId, ' '.join(args[1:]))
        
        else:
            self.send_message(message.roomId, f"❓ Unknown command: {command}")
    
    def send_message(self, room_id, text, attachments=None):
        """Send message"""
        try:
            if attachments:
                self.api.messages.create(
                    roomId=room_id,
                    text=text,
                    attachments=attachments
                )
            else:
                self.api.messages.create(roomId=room_id, text=text)
        except Exception as e:
            print(f"Message send error: {e}")
    
    def send_adaptive_card(self, room_id, card_data):
        """Send Adaptive Card"""
        attachments = [{
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": card_data
        }]
        
        self.send_message(room_id, "Card-based message:", attachments)
    
    def send_help_message(self, room_id):
        """Send help message"""
        help_card = {
            "type": "AdaptiveCard",
            "version": "1.4",
            "body": [
                {
                    "type": "TextBlock",
                    "text": "🤖 Webex Python Bot",
                    "weight": "Bolder",
                    "size": "Medium"
                },
                {
                    "type": "FactSet",
                    "facts": [
                        {"title": "/ping", "value": "Connection test"},
                        {"title": "/status", "value": "System status"},
                        {"title": "/weather [city]", "value": "Weather information"},
                        {"title": "/meeting create [title]", "value": "Create meeting"}
                    ]
                }
            ]
        }
        
        self.send_adaptive_card(room_id, help_card)
    
    def send_system_status(self, room_id):
        """Send system status"""
        import psutil
        import platform
        
        cpu_percent = psutil.cpu_percent()
        memory = psutil.virtual_memory()
        
        status_card = {
            "type": "AdaptiveCard", 
            "version": "1.4",
            "body": [
                {
                    "type": "TextBlock",
                    "text": "📊 System Status",
                    "weight": "Bolder", 
                    "size": "Medium"
                },
                {
                    "type": "FactSet",
                    "facts": [
                        {"title": "CPU Usage", "value": f"{cpu_percent}%"},
                        {"title": "Memory Usage", "value": f"{memory.percent}%"},
                        {"title": "Platform", "value": f"{platform.system()} {platform.machine()}"},
                        {"title": "Python", "value": platform.python_version()}
                    ]
                }
            ]
        }
        
        self.send_adaptive_card(room_id, status_card)
    
    def create_meeting(self, room_id, title):
        """Create meeting"""
        try:
            from datetime import datetime, timedelta
            
            # Create 1-hour meeting starting 10 minutes from now
            start_time = datetime.now() + timedelta(minutes=10)
            end_time = start_time + timedelta(hours=1)
            
            meeting_data = {
                'title': title or 'Python Bot Created Meeting',
                'start': start_time.isoformat(),
                'end': end_time.isoformat(),
                'timezone': 'UTC',
                'enabledAutoRecordMeeting': False,
                'allowAnyUserToBeCoHost': True
            }
            
            meeting = self.api.meetings.create(**meeting_data)
            
            meeting_card = {
                "type": "AdaptiveCard",
                "version": "1.4", 
                "body": [
                    {
                        "type": "TextBlock",
                        "text": "🎥 Meeting Created",
                        "weight": "Bolder",
                        "size": "Medium"
                    },
                    {
                        "type": "FactSet",
                        "facts": [
                            {"title": "Title", "value": meeting.title},
                            {"title": "Start Time", "value": start_time.strftime("%Y-%m-%d %H:%M")},
                            {"title": "Meeting Number", "value": meeting.meetingNumber},
                            {"title": "Join URL", "value": meeting.webLink}
                        ]
                    }
                ],
                "actions": [
                    {
                        "type": "Action.OpenUrl",
                        "title": "Join Meeting",
                        "url": meeting.webLink
                    }
                ]
            }
            
            self.send_adaptive_card(room_id, meeting_card)
            
        except Exception as e:
            print(f"Meeting creation error: {e}")
            self.send_message(room_id, "❌ Failed to create meeting")

# Flask Webhook Server
app = Flask(__name__)
bot = None

@app.route('/webhooks/messages', methods=['POST'])
def handle_message_webhook():
    """Message webhook processing"""
    webhook_data = request.json
    
    if webhook_data.get('event') == 'created':
        bot.handle_message(webhook_data['data'])
    
    return jsonify({'status': 'success'})

@app.route('/api/alert', methods=['POST'])
def handle_alert():
    """External system alert processing"""
    alert_data = request.json
    room_id = alert_data.get('roomId')
    
    if not room_id:
        return jsonify({'error': 'roomId required'}), 400
    
    try:
        alert_card = {
            "type": "AdaptiveCard",
            "version": "1.4",
            "body": [
                {
                    "type": "TextBlock", 
                    "text": f"🚨 {alert_data.get('title', 'System Alert')}",
                    "weight": "Bolder",
                    "size": "Medium"
                },
                {
                    "type": "TextBlock",
                    "text": alert_data.get('description', ''),
                    "wrap": True
                },
                {
                    "type": "FactSet",
                    "facts": [
                        {"title": "Severity", "value": alert_data.get('severity', 'Unknown')},
                        {"title": "Service", "value": alert_data.get('service', 'Unknown')},
                        {"title": "Timestamp", "value": alert_data.get('timestamp', 'Unknown')}
                    ]
                }
            ]
        }
        
        bot.send_adaptive_card(room_id, alert_card)
        return jsonify({'status': 'success'})
        
    except Exception as e:
        print(f"Alert send error: {e}")
        return jsonify({'error': 'Failed to send alert'}), 500

@app.route('/health', methods=['GET'])
def health_check():
    """Health check"""
    return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})

def run_flask_app():
    """Run Flask app in separate thread"""
    app.run(host='0.0.0.0', port=int(os.getenv('PORT', 3000)), debug=False)

if __name__ == '__main__':
    access_token = os.getenv('WEBEX_ACCESS_TOKEN')
    
    if not access_token:
        print("❌ WEBEX_ACCESS_TOKEN not set")
        exit(1)
    
    # Bot initialization
    bot = WebexPythonBot(access_token)
    print("🚀 Webex Python Bot started")
    
    # Start Flask server in separate thread
    flask_thread = threading.Thread(target=run_flask_app)
    flask_thread.daemon = True
    flask_thread.start()
    
    # Main thread stays alive
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("🛑 Stopping bot")

Environment Variables Configuration

# .env file
# Webex API settings
WEBEX_ACCESS_TOKEN=your-webex-access-token
WEBEX_WEBHOOK_SECRET=your-webhook-secret

# Webhook URL (production environment)
WEBHOOK_URL=https://your-domain.com/webhooks

# GitHub integration
GITHUB_TOKEN=your-github-token
GITHUB_NOTIFICATION_ROOM=your-room-id

# External API integration
WEATHER_API_KEY=your-weather-api-key

# Server settings
PORT=3000
NODE_ENV=production

# Database (optional)
DATABASE_URL=your-database-url

# Cisco Webex Device integration (optional)
WEBEX_DEVICE_USERNAME=your-device-username
WEBEX_DEVICE_PASSWORD=your-device-password

# Enterprise settings
WEBEX_ORG_ID=your-organization-id
[email protected]

Docker Configuration Example

# Dockerfile
FROM node:18-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy application files
COPY . .

# Environment variables
ENV NODE_ENV=production
ENV PORT=3000

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Start application
CMD ["npm", "start"]

Docker Compose Configuration

# docker-compose.yml
version: '3.8'

services:
  webex-bot:
    build: .
    container_name: webex-bot
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - WEBEX_ACCESS_TOKEN=${WEBEX_ACCESS_TOKEN}
      - WEBEX_WEBHOOK_SECRET=${WEBEX_WEBHOOK_SECRET}
      - WEBHOOK_URL=${WEBHOOK_URL}
      - WEATHER_API_KEY=${WEATHER_API_KEY}
      - GITHUB_TOKEN=${GITHUB_TOKEN}
      - PORT=3000
    volumes:
      - ./logs:/app/logs
    networks:
      - webex-network

  # Optional: Redis for session management
  redis:
    image: redis:7-alpine
    container_name: webex-redis
    restart: unless-stopped
    volumes:
      - redis_data:/data
    networks:
      - webex-network

  # Optional: PostgreSQL for data storage
  postgres:
    image: postgres:15-alpine
    container_name: webex-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: webex_bot
      POSTGRES_USER: webex
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - webex-network

volumes:
  redis_data:
  postgres_data:

networks:
  webex-network:
    driver: bridge

CI/CD Pipeline Integration

# .github/workflows/webex-notification.yml
name: Webex Deployment Notification

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  notify-deployment:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Deploy to production
        run: |
          # Deployment process
          echo "Deploying to production..."

      - name: Notify Webex on success
        if: success()
        run: |
          curl -X POST "${{ secrets.WEBEX_WEBHOOK_URL }}" \
            -H "Content-Type: application/json" \
            -d '{
              "roomId": "${{ secrets.WEBEX_ROOM_ID }}",
              "alertData": {
                "title": "Deployment Successful",
                "description": "Production deployment completed successfully",
                "severity": "info",
                "service": "GitHub Actions",
                "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
                "dashboardUrl": "https://github.com/${{ github.repository }}/actions"
              }
            }'

      - name: Notify Webex on failure
        if: failure()
        run: |
          curl -X POST "${{ secrets.WEBEX_WEBHOOK_URL }}" \
            -H "Content-Type: application/json" \
            -d '{
              "roomId": "${{ secrets.WEBEX_ROOM_ID }}",
              "alertData": {
                "title": "Deployment Failed",
                "description": "Production deployment failed",
                "severity": "critical",
                "service": "GitHub Actions",
                "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
                "dashboardUrl": "https://github.com/${{ github.repository }}/actions"
              }
            }'