Handling chatbot webhooks and events

In this section, you'll implement a webhook handler to process real-time events sent from Chat to your chatbot. These events include bot installation, message notifications, and app deauthorization. You'll also handle Zoom's URL validation flow to verify your webhook endpoint.

By the end of this section, you’ll have:

  • A secure webhook handler for Chat events
  • Support for responding to bot messages and system events
  • Built-in validation and health check for endpoint reliability

Implement a webhook event handler

Set up a route to handle incoming Zoom chatbot events, validate requests, and process supported event types.

This webhook handler enables real-time communication between your Zoom chatbot and users, allowing your app to respond to messages, handle installations, and maintain a reliable connection with Chat.

Event Types Covered

Event typeDescription
bot_notificationTriggered when a user sends a message to the chatbot
bot_installedSent when the chatbot is installed in a Chat workspace
app_deauthorizedSent when the app is uninstalled
endpoint.url_validationUsed by Zoom to validate and verify the webhook endpoint

File Path

routes/zoom-webhookHandler.js

Code Snippet

import express from "express";
import { callAnthropicAPI } from "../utils/anthropic.js";
import {
    validateWebhookPayload,
    createValidationMiddleware,
} from "../utils/validation.js";
const router = express.Router();
/**
 * Handles Chat webhook events
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 */
async function handleZoomWebhook(req, res) {
    try {
        const { event, payload } = req.body;
        console.log(`Received Zoom webhook event: ${event}`);
        console.log("Payload:", payload);
        const toJid = payload?.toJid;
        const message = payload?.cmd || payload?.message || "";
        switch (event) {
            case "endpoint.url_validation":
                console.log("Validating webhook endpoint URL");
                return res.status(200).json({
                    message: {
                        plainToken: payload?.plainToken || "missing_token",
                    },
                });
            case "app_deauthorized":
                console.log("Chat bot uninstalled");
                break;
            case "bot_installed":
                console.log("Chat bot installed successfully");
                break;
            case "bot_notification":
                console.log("Processing bot notification from Chat");
                // Ensure sendChatMessage is imported or defined to use here
                sendChatMessage(toJid, message);
                break;
            default:
                console.log(`Unsupported Zoom webhook event type: ${event}`);
                break;
        }
        res.status(200).json({
            success: true,
            message: "Event processed successfully",
            event,
        });
    } catch (error) {
        console.error("Error handling Zoom webhook event:", error);
        res.status(500).json({
            success: false,
            error: "Internal Server Error",
            message: error.message,
        });
    }
}
// Webhook route with validation
router.post(
    "/",
    createValidationMiddleware(validateWebhookPayload),
    handleZoomWebhook,
);
// Health check endpoint
router.get("/health", (_req, res) => {
    res.status(200).json({
        status: "healthy",
        timestamp: new Date().toISOString(),
        service: "zoom-webhook-handler",
    });
});
export default router;