Skip to content
Esc
↑↓navigate↵open⌘Jpreview

Teach your Chat SDK app to speak Zoom.

Mentions, DMs and slash commands arrive as ordinary Chat threads, andthread.post() sends the answer back to Zoom Team Chat.

A community package. Not published or supported by Zoom.

release-engTeam Chat

Aradhya CP 9:41

@deploybot status api-gateway

deploybot app 9:41

api-gateway is live in production• Released 12 minutes ago by aradhya@acme.com• Build 4812 · commit 9f2ac1c• Health checks passing in all three regions

bot_notification → thread.post() → POST /v2/im/chat/messages

pnpm add zoom-chat-adapter chat @chat-adapter/state-memory
Add Zoom Team Chat support to this project with the community adapter zoom-chat-adapter.

1. Install it: pnpm add zoom-chat-adapter chat @chat-adapter/state-memory
2. Read the docs first: https://zoom-chat-adapter.vercel.app/llms.txt indexes
   every page. Start with https://zoom-chat-adapter.vercel.app/quickstart.md,
   then webhooks.md and messaging.md — every page serves Markdown at .md.
3. Create the Chat instance with a state adapter, then one POST route that passes
   the untouched Request to bot.webhooks.zoom — reading the body first breaks
   Zoom's signature check.
4. Read limitations.mdx before designing around threads, history or reactions:
   several of those calls throw on purpose.

One webhook in, one Chat thread out.

Zoom sends every event for your app to a single endpoint. The adapter verifies it, works out whether it came from a channel or a DM, and hands your handlers an ordinary Chat thread.

receive
Every event, one routeThe endpoint answers Zoom's setup challenge, checks the HMAC signature against the raw request bytes, and turns bot_notification and team_chat.app_mention into Chat threads. Pass waitUntil and Zoom gets its 200 immediately.
send
Post, edit, deleteReplies go out as Zoom Markdown with is_markdown_support, so bold, links, quotes, and images survive. A reply can be attached to a parent message through metadata.replyTo.
identify
Channel or DM, from the JIDA thread ID carries the Zoom JID it came from. Anything ending in @conference.xmpp.zoom.us is a channel; everything else is a direct message.

Wire it up

Two files: the app, and the route Zoom posts to. Hand the request over untouched — reading the body first breaks signature checks for every event.

lib/bot.ts
import { createMemoryState } from "@chat-adapter/state-memory";
import { Chat } from "chat";
import { createZoomAdapter } from "zoom-chat-adapter";

export const bot = new Chat({
  userName: "deploybot",
  adapters: { zoom: createZoomAdapter() },
  state: createMemoryState(),
});

bot.onNewMention(async (thread, message) => {
  await thread.post(`Looking into ${message.text}…`);
});
app/api/webhooks/zoom/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";

export async function POST(request: Request) {
  return bot.webhooks.zoom(request, { waitUntil: after });
}

Credentials come from the environment, so createZoomAdapter() takes no arguments in most apps. A missing one throws at startup and names the variable to set.

What this adapter does, and what it refuses to pretend.

Zoom's Team Chat app API covers less ground than Slack's or Discord's. Rather than fake the gaps, the unimplemented calls throw where you can see them.

CallWhat happens
thread.post()Works. Sends a message to the channel or DM the thread came from.
editMessage / deleteMessageWorks. Edits and removes a message the app sent.
metadata.replyToWorks. Posts the message as a reply under a parent message.
startTyping()Accepted, does nothing. Zoom has no typing indicator, so the call returns quietly.
fetchMessages / fetchThreadThrows. No message history. Keep what you need in your own state adapter.
addReaction / removeReactionThrows. Bots cannot react to messages in Zoom Team Chat.
parseMessage()Throws. Not implemented. Incoming messages are already parsed for you.

The whole adapter, written down.

Setup, webhooks, messaging, thread IDs, the full API, and an honest account of what Zoom's platform will not do. Every page also serves Markdown to your agent by adding .md to its URL.

Read the docs