Skip to content

zoom/chatbot-services-nodejs-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zoom Chat Bot — Node.js Sample App

A working sample application for building a Zoom Chat bot using Node.js and Express. The bot demonstrates two complete flows: an AI Research Assistant powered by the Anthropic Claude API, and an Employee Appreciation multi-step form flow.


What This Bot Does

  • Responds to messages and button interactions in Zoom Chat
  • Routes all responses as threaded replies within the originating conversation
  • Sends interactive cards with buttons and multi-field forms
  • Maintains per-user session state across a multi-step flow
  • Integrates with the Anthropic Claude API for AI-generated responses
  • Supports both direct messages and channel conversations

Services

Service Trigger Description
Research Assistant Click button or type menu Ask a question, draft a professional response
Employee Appreciation Click button or type menu Multi-step form to recognize a teammate

Text Commands

Command What it does
menu, help, start, services Show the main service menu
cancel, stop, quit, exit Stop the current flow

Prerequisites

  • Zoom Developer Account
  • A General OAuth App created in the Zoom App Marketplace
  • An Upstash Redis database (free tier works) — the bot stores flow state, form drafts, conversation history, and the cached Zoom token in Redis
  • Node.js v20 or higher (the app uses native fetch — no extra HTTP library needed)
  • ngrok or another tunneling tool for local development

Fastest start: create the Zoom app from the manifest

The quickest way to a working app is to upload the ready-made manifest instead of clicking through every Marketplace setting by hand.

➡️ See 0-app-manifest/ — it contains app-manifest.json (scopes, chatbot subscription, events, webview, and redirect URIs already configured) plus a short guide to importing it in the Zoom App Marketplace. Replace example.ngrok.app with your tunnel URL, upload, and your app is configured.

Then come back here to run the server locally.


Getting Started

1. Clone the repository

git clone https://github.com/zoom/chatbot-services-nodejs-sample.git
cd chatbot-services-nodejs-sample

2. Install dependencies

npm install

3. Set up your environment variables

Copy the example file and fill in your credentials:

cp .env.example .env
# .env

# ─── Zoom App Credentials ─────────────────────────────────────────────────────
ZOOM_CLIENT_ID=your_zoom_client_id_here
ZOOM_CLIENT_SECRET=your_zoom_client_secret_here
ZOOM_ACCOUNT_ID=your_zoom_account_id_here

# ─── Zoom Chat Bot Settings ──────────────────────────────────────────────
ZOOM_BOT_JID=your_bot_jid_here
ZOOM_VERIFICATION_TOKEN=your_verification_token_here

# ─── OAuth Redirect ───────────────────────────────────────────────────────────
ZOOM_REDIRECT_URI=https://your-ngrok-url.ngrok-free.app/auth/callback

# ─── Anthropic / Claude ───────────────────────────────────────────────────────
ANTHROPIC_API_KEY=your_anthropic_api_key_here
ANTHROPIC_MODEL=claude-sonnet-4-6

# ─── Server ───────────────────────────────────────────────────────────────────
PORT=4000
SESSION_SECRET=change-this-to-a-long-random-string-in-production
FRONTEND_ORIGIN=http://localhost:3000

Never commit your .env file. It is already listed in .gitignore.

4. Expose your local server with ngrok

ngrok http 4000

Copy the HTTPS forwarding URL (e.g. https://abc123.ngrok-free.app) and use it in step 6.

5. Start the server

npm start

The server binds to port 4000 and waits silently for incoming webhook events — this is normal. You will see log output when Zoom sends events.

6. Configure your Zoom App

In the Zoom App Marketplace, open your app and set:

  • Chatbot Endpoint URL: https://your-ngrok-url/webhooks
  • OAuth Redirect URL: https://your-ngrok-url/auth/callback
  • Chat Subscription: enabled

How It Works

Webhook Event Flow

Every interaction starts with Zoom sending a POST request to /webhooks. The webhook handler verifies the request signature, then routes the event by type:

Zoom sends webhook → POST /webhooks
  │
  ├── Timestamp checked (±5 min) + signature verified (HMAC-SHA256, constant-time)
  │
  └── Switch on event type:
        bot_notification          → user typed a message (direct message)
        interactive_message_actions → user clicked a button
        chat_message.submit       → user submitted a form
        team_chat.app_invited     → bot added to a channel
        team_chat.app_mention     → bot @mentioned in a channel (intent detection → routes)
        chat_message.replied      → user replied in a channel thread (continues active flows)
        chat_message.sent         → message posted in a channel (continues active flows only)
        bot_installed             → bot installed by a user
        endpoint.url_validation   → Zoom verifying the webhook URL

Threading

All bot responses are sent as threaded replies within the conversation where the interaction started. The replyMainMessageId from each incoming event is passed through the entire handler chain and set as reply_to in the Zoom API request.

For multi-step flows, the thread root is stored in session state at the start of the flow so that later steps (like form submission) can thread correctly even when the event type changes.

Research Assistant Flow

User: "menu"  →  Bot sends service menu card
User clicks "Research Assistant"  →  Bot sends research sub-menu
User clicks "Ask a Question"  →  Bot: "What question would you like to research?"
User types question  →  Claude API streams response  →  Bot sends answer (threaded)

Conversation history is maintained per user (last 20 messages) so Claude has context for follow-up questions.

Employee Appreciation Flow

User clicks "Employee Appreciation"
  → Bot: "Who would you like to appreciate? Enter their full name."

User types name
  → Bot: "Enter the employee's email address."

User types email
  → Bot sends multi-field form card (What was done / Why it mattered / Who it helped)

User fills form and clicks Submit
  → chat_message.submit event fires
  → Bot sends confirmation card with colored fields (threaded)

Note on form field values: The submitted field values arrive in the chat_message.submit event's submit_items. See src/handlers/submit-handler.js.

Environment Variables Reference

Variable Required Description
ZOOM_CLIENT_ID Yes OAuth app Client ID
ZOOM_CLIENT_SECRET Yes OAuth app Client Secret
ZOOM_ACCOUNT_ID Yes Zoom Account ID
ZOOM_BOT_JID Yes Bot's Zoom JID (from Chat Subscription)
ZOOM_BOT_NAME No Bot display name (e.g. Zoom Service Chatbot). Used to strip the bot's @mention from channel messages so user input parses correctly
ZOOM_VERIFICATION_TOKEN Yes Webhook signature verification token
ZOOM_REDIRECT_URI Yes OAuth callback URL (must match Marketplace setting)
WEBVIEW_URL No Public URL of the Zoom App webview for the "Open App" button (default: origin of ZOOM_REDIRECT_URI + /webview)
ANTHROPIC_API_KEY Yes Anthropic API key
ANTHROPIC_MODEL No Claude model to use (default: claude-sonnet-4-6)
UPSTASH_REDIS_REST_URL Yes Upstash Redis REST URL (backs state, form drafts, history, token cache)
UPSTASH_REDIS_REST_TOKEN Yes Upstash Redis REST token
NODE_ENV No Set to production in deployment — hides internal error details and enables secure cookies
PORT No Server port (default: 4000)
SESSION_SECRET Yes in prod Session cookie secret. Required when NODE_ENV=production (dev falls back to a placeholder)
FRONTEND_ORIGIN No CORS allowed origin (default: http://localhost:3000)

Need Help?

About

A working sample application for building a Zoom Team Chat bot using Node.js and Express. The bot demonstrates two flows: an AI Research Assistant powered by the Anthropic Claude API, and an Employee Appreciation multi-step form flow.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors