Files
2026-07-13 12:20:06 +08:00

14 KiB

Sample Applications Analysis

Analysis of 10 official Zoom Team Chat sample applications, extracted patterns, and best practices.

Sample Overview

Sample Language Complexity Best For
chatbot-nodejs-quickstart Node.js Beginner Start here - Tutorial series
zoom-chatbot-claude-sample Node.js Intermediate LLM integration pattern
unsplash-chatbot Node.js Intermediate API integration + database
zoom-erp-chatbot-sample Node.js Advanced Enterprise integration
task-manager-sample Node.js Advanced Full CRUD application
zoom-cohere-chatbot-sample Node.js Intermediate Cohere LLM integration
zoom-cerebras-chatbot-sample Node.js Intermediate Cerebras LLM integration
zoom-team-chat-shortcut-sample Node.js Intermediate Shortcuts and UI elements
zoom-teams-chat-snowflake-sample Node.js Advanced Snowflake data integration
rivet-javascript-sample Node.js Intermediate Rivet SDK usage

1. chatbot-nodejs-quickstart

Repository: https://github.com/zoom/chatbot-nodejs-quickstart

Description: Official tutorial series covering 9 episodes from setup to advanced features.

Key Features:

  • Setup & Send Messages
  • Handle Events
  • Slash Commands
  • Markdown & Emojis
  • Reactions & Interactive Messages
  • Threaded Replies
  • Search Messages via API
  • Scheduling Messages
  • Zoom Workplace App Integration

Project Structure:

chatbot-nodejs-quickstart/
├── routes/
│   ├── zoom-webhookHandler.js     # Webhook event handling
│   └── oauth-routes.js             # OAuth flow
├── utils/
│   ├── zoom-api.js                 # API helper functions
│   ├── zoom-chatbot-auth.js        # Token generation
│   └── validation.js               # Webhook signature verification
├── views/                          # EJS templates
├── server.js                       # Express app
└── .env.example                    # Environment variables

Key Patterns:

Webhook Handler Pattern

async function handleZoomWebhook(req, res) {
  verifyZoomWebhookSignature(req);
  
  const { event, payload } = req.body;
  
  switch (event) {
    case 'bot_notification':
      return handleBotNotification(payload, res);
    case 'interactive_message_actions':
      return handleButtonClick(payload, res);
    // ... more cases
  }
}

Token Generation

async function getChatbotToken() {
  const credentials = Buffer.from(
    `${CLIENT_ID}:${CLIENT_SECRET}`
  ).toString('base64');
  
  const response = await fetch('https://zoom.us/oauth/token', {
    method: 'POST',
    headers: { 'Authorization': `Basic ${credentials}` },
    body: 'grant_type=client_credentials'
  });
  
  return (await response.json()).access_token;
}

Best Practices:

  • Signature verification on all webhooks
  • Environment variables for credentials
  • Modular route structure
  • Error handling with try/catch
  • Immediate webhook response (200 status)

Recommended For: First-time chatbot developers

2. zoom-chatbot-claude-sample

Repository: https://github.com/zoom/zoom-chatbot-claude-sample

Description: AI-powered chatbot using Anthropic Claude for natural language responses.

Key Features:

  • Claude API integration
  • Conversation history tracking
  • Streaming responses (optional)
  • Context management

LLM Integration Pattern:

case 'bot_notification': {
  const { toJid, cmd, accountId } = payload;
  
  // Call Claude API
  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    messages: [{ role: 'user', content: cmd }]
  });
  
  const llmResponse = response.content[0].text;
  
  // Send back to Zoom
  await sendChatbotMessage(toJid, accountId, {
    body: [{ type: 'message', text: llmResponse }]
  });
}

Conversation History Pattern:

const conversationHistory = new Map();

function addToHistory(userId, role, content) {
  if (!conversationHistory.has(userId)) {
    conversationHistory.set(userId, []);
  }
  conversationHistory.get(userId).push({ role, content });
}

// In bot_notification handler
const history = conversationHistory.get(userId) || [];
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-20250514',
  messages: history
});

Environment Variables:

ANTHROPIC_API_KEY=your_api_key_here
ZOOM_CLIENT_ID=...
ZOOM_CLIENT_SECRET=...
ZOOM_BOT_JID=...

Recommended For: Building AI assistants

3. unsplash-chatbot

Repository: https://github.com/zoom/unsplash-chatbot

Description: Image search bot integrating Unsplash API with database storage.

Key Features:

  • Third-party API integration (Unsplash)
  • Database persistence (SQLite/PostgreSQL)
  • Image search and display
  • User preference storage

Database Schema:

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  zoom_user_id TEXT UNIQUE,
  preferences TEXT
);

CREATE TABLE searches (
  id INTEGER PRIMARY KEY,
  user_id INTEGER,
  query TEXT,
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

Image Display Pattern:

{
  "content": {
    "head": { "text": "Image Results" },
    "body": [
      {
        "type": "attachments",
        "img_url": imageData.urls.regular,
        "resource_url": imageData.links.html,
        "information": {
          "title": { "text": imageData.description },
          "description": { "text": `Photo by ${imageData.user.name}` }
        }
      }
    ]
  }
}

Best Practices:

  • API rate limiting handling
  • Error handling for external APIs
  • Database connection pooling
  • User data privacy

Recommended For: External API integration patterns

4. zoom-erp-chatbot-sample

Repository: https://github.com/zoom/zoom-erp-chatbot-sample

Description: Enterprise Resource Planning integration with scheduled alerts.

Key Features:

  • Oracle ERP API integration
  • Scheduled notifications (cron)
  • Approval workflows
  • Threaded conversations

Scheduled Alerts Pattern:

const cron = require('node-cron');

// Daily report at 9 AM
cron.schedule('0 9 * * *', async () => {
  const report = await getERPReport();
  
  await sendChatbotMessage(channelJid, accountId, {
    head: { "text": "Daily ERP Report" },
    body: [
      { "type": "fields", "items": report.fields },
      {
        "type": "actions",
        "items": [
          { "text": "View Details", "value": "view_report" }
        ]
      }
    ]
  });
});

Approval Workflow Pattern:

// Send approval request
{
  "head": { "text": "Expense Approval Required" },
  "body": [
    { "type": "fields", "items": expenseFields },
    {
      "type": "actions",
      "items": [
        { "text": "Approve", "value": `approve_${expenseId}`, "style": "Primary" },
        { "text": "Reject", "value": `reject_${expenseId}`, "style": "Danger" }
      ]
    }
  ]
}

// Handle button click
case 'interactive_message_actions': {
  const action = payload.actionItem.value;
  const [decision, expenseId] = action.split('_');
  
  await updateERPStatus(expenseId, decision);
  await sendConfirmation(payload.toJid, decision);
}

Recommended For: Enterprise integrations, workflows

5. task-manager-sample

Repository: https://github.com/zoom/task-manager-sample

Description: Full-featured task management application with CRUD operations.

Key Features:

  • Create, read, update, delete tasks
  • Task assignment
  • Due date tracking
  • Status management
  • Persistent storage

CRUD Pattern:

// CREATE
case 'bot_notification': {
  if (cmd.startsWith('create task')) {
    const taskData = parseTaskCommand(cmd);
    const task = await db.createTask(taskData);
    await sendTaskCreatedMessage(toJid, accountId, task);
  }
}

// READ
case 'interactive_message_actions': {
  if (actionItem.value.startsWith('view_task')) {
    const taskId = actionItem.value.split('_')[2];
    const task = await db.getTask(taskId);
    await sendTaskDetails(toJid, accountId, task);
  }
}

// UPDATE
case 'interactive_message_actions': {
  if (actionItem.value.startsWith('complete_task')) {
    const taskId = actionItem.value.split('_')[2];
    await db.updateTaskStatus(taskId, 'completed');
    await sendStatusUpdate(toJid, accountId, taskId);
  }
}

// DELETE
case 'interactive_message_actions': {
  if (actionItem.value.startsWith('delete_task')) {
    const taskId = actionItem.value.split('_')[2];
    await db.deleteTask(taskId);
    await sendDeletionConfirmation(toJid, accountId, taskId);
  }
}

Recommended For: Full application architecture

Common Patterns Across Samples

1. Environment Variable Management

All samples use .env files with similar structure:

# Authentication
ZOOM_CLIENT_ID=
ZOOM_CLIENT_SECRET=
ZOOM_BOT_JID=
ZOOM_VERIFICATION_TOKEN=
ZOOM_ACCOUNT_ID=

# Third-party APIs (if applicable)
ANTHROPIC_API_KEY=
UNSPLASH_ACCESS_KEY=

# Server
PORT=4000
NODE_ENV=development

2. Project Structure

Common folder organization:

sample-app/
├── routes/
│   ├── webhook.js           # Webhook handlers
│   └── oauth.js             # OAuth flows (if needed)
├── utils/
│   ├── zoom-api.js          # Zoom API wrappers
│   ├── auth.js              # Token management
│   └── validation.js        # Input validation
├── models/                  # Database models (if applicable)
├── views/                   # Frontend templates (if applicable)
├── server.js                # Express app
├── .env.example
└── package.json

3. Webhook Verification

All samples verify webhook signatures:

function verifyWebhook(req) {
  const signature = req.headers['x-zm-signature'];
  const timestamp = req.headers['x-zm-request-timestamp'];
  const message = `v0:${timestamp}:${JSON.stringify(req.body)}`;
  
  const hash = crypto.createHmac('sha256', SECRET_TOKEN)
    .update(message)
    .digest('hex');
  
  return signature === `v0=${hash}`;
}

4. Error Handling

Consistent error handling pattern:

app.post('/webhook', async (req, res) => {
  try {
    verifyWebhook(req);
    await handleWebhook(req.body);
    res.status(200).json({ success: true });
  } catch (error) {
    console.error('Webhook error:', error);
    
    if (error.message.includes('signature')) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    res.status(500).json({ error: 'Internal server error' });
  }
});

5. Async Webhook Processing

Respond immediately, process async:

app.post('/webhook', (req, res) => {
  // Respond immediately
  res.status(200).json({ success: true });
  
  // Process asynchronously
  processWebhookAsync(req.body).catch(error => {
    console.error('Async processing error:', error);
  });
});

Architecture Lessons

Chatbot Lifecycle

Common lifecycle across all samples:

1. User Action (slash command, button click, message)
   ↓
2. Zoom sends webhook to Bot Endpoint URL
   ↓
3. Server verifies signature
   ↓
4. Server responds 200 (immediately)
   ↓
5. Server processes request (async)
   ↓
6. Server calls external APIs if needed
   ↓
7. Server sends chatbot message back to Zoom

State Management

Simple bots: In-memory state (Map/Object) Production bots: Database (PostgreSQL, MongoDB, Redis)

// Simple (development)
const userState = new Map();

// Production
const userState = {
  async get(userId) {
    return await db.query('SELECT * FROM user_state WHERE user_id = $1', [userId]);
  },
  async set(userId, state) {
    return await db.query('INSERT INTO user_state (user_id, state) VALUES ($1, $2) ON CONFLICT (user_id) DO UPDATE SET state = $2', [userId, state]);
  }
};

Deprecation Notes

Some samples may use deprecated patterns:

Old Pattern (Don't Use)

// Hardcoded credentials
const CLIENT_ID = 'abc123';

New Pattern (Use This)

// Environment variables
const CLIENT_ID = process.env.ZOOM_CLIENT_ID;

Old Pattern (Don't Use)

// Synchronous webhook processing (may timeout)
app.post('/webhook', async (req, res) => {
  await longRunningProcess();
  res.status(200).json({ success: true });
});

New Pattern (Use This)

// Async processing
app.post('/webhook', (req, res) => {
  res.status(200).json({ success: true });
  longRunningProcess().catch(console.error);
});

Sample Selection Guide

Choose chatbot-nodejs-quickstart if:

  • You're new to Zoom chatbots
  • You want a tutorial series
  • You need step-by-step guidance

Choose zoom-chatbot-claude-sample if:

  • You want to integrate an LLM
  • You need conversational AI
  • You want to see LLM integration patterns

Choose unsplash-chatbot if:

  • You need to integrate external APIs
  • You want database patterns
  • You need user preference storage

Choose zoom-erp-chatbot-sample if:

  • You're building enterprise integrations
  • You need scheduled notifications
  • You want approval workflows

Choose task-manager-sample if:

  • You want a full CRUD application
  • You need complex state management
  • You want to see production architecture

Next Steps

Resources