Zoom Team Chat adapter for the chat SDK.
npm install chat-adapter-zoom chatimport { Chat } from "chat";
import { createZoomAdapter } from "chat-adapter-zoom";
const bot = new Chat({
adapters: { zoom: createZoomAdapter() },
state,
});
bot.onNewMention(async (thread, message) => {
await thread.subscribe();
await thread.post(`Hi ${message.author.userName}!`);
});
bot.onSubscribedMessage(async (thread, message) => {
await thread.post(`You said: ${message.text}`);
});createZoomAdapter() reads credentials from environment variables by default. You can also pass them explicitly.
| Option | Env var | Description |
|---|---|---|
clientId |
ZOOM_CLIENT_ID |
OAuth app Client ID |
clientSecret |
ZOOM_CLIENT_SECRET |
OAuth app Client Secret |
accountId |
ZOOM_ACCOUNT_ID |
Account ID for S2S token exchange |
robotJid |
ZOOM_ROBOT_JID |
Bot JID from Team Chat Subscription |
webhookSecretToken |
ZOOM_WEBHOOK_SECRET_TOKEN |
Webhook Secret Token |
userName |
ZOOM_BOT_USERNAME |
Display name for the bot (default: "zoom-bot") |
All five credential fields are required. A ValidationError is thrown at startup if any are missing.
Wire up the webhook route in your framework. With Next.js App Router:
// app/api/webhooks/zoom/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";
export async function POST(request: Request): Promise<Response> {
return bot.webhooks.zoom(request, {
waitUntil: (task) => after(() => task),
});
}Set the Bot Endpoint URL in your Zoom Marketplace app to https://your-deployment/api/webhooks/zoom.
The adapter handles Zoom's CRC URL validation challenge automatically — no extra route needed.
- Create a General App (Admin-managed) at marketplace.zoom.us
- Enable Team Chat Subscription under Features
- Copy credentials to your environment:
- Client ID / Secret from App Credentials
- Account ID from App Credentials
- Bot JID from Team Chat Subscription
- Webhook Secret Token from Features → Access → Token
- Add scope:
imchat:bot - Set Bot Endpoint URL to your webhook route
ZOOM_CLIENT_ID=your_client_id
ZOOM_CLIENT_SECRET=your_client_secret
ZOOM_ACCOUNT_ID=your_account_id
ZOOM_ROBOT_JID=your_robot_jid@xmpp.zoom.us
ZOOM_WEBHOOK_SECRET_TOKEN=your_webhook_secret_token- Webhook verification — CRC URL validation (HMAC-SHA256) + per-request signature verification with timing-safe comparison and 5-minute replay protection
- Inbound events —
bot_notificationandteam_chat.app_mentionnormalized to SDKMessage - Outbound —
thread.post(),thread.edit(),thread.delete()via/v2/im/chat/messages - Markdown — bidirectional Zoom markdown ↔ mdast (bold, italic, code, links, lists, blockquotes, inline code)
- Emoji — Unicode rendering via
convertEmojiPlaceholders - DM detection —
isDM(threadId)based on JID suffix - Auth — S2S OAuth
client_credentialswith automatic token caching and 60-second early-expiry refresh
| Limitation | Detail |
|---|---|
| Thread subscription | reply_to / parent_message_id not present in bot_notification |
| DM thread replies | chat_message.replied does not fire for 1:1 DM thread replies |
| Typing indicators | No Zoom API exists — startTyping() is a silent no-op |
| Reactions | addReaction() / removeReaction() throw NotImplementedError |
fetchMessages |
Not yet implemented |
A full Next.js example is in examples/nextjs/.
MIT — see LICENSE.