Configuration
Every option, the environment variable behind it, and what happens when one is missing.
createZoomAdapter() reads everything from the environment, so most apps call
it with no arguments at all:
const adapter = createZoomAdapter();
Anything you pass explicitly wins over the environment:
createZoomAdapter({
clientId: "your-client-id",
clientSecret: "your-client-secret",
accountId: "your-account-id",
robotJid: "bot-jid@xmpp.zoom.us",
webhookSecretToken: "your-secret-token",
});
Options
| Option | Environment variable | Required | Notes |
|---|---|---|---|
clientId |
ZOOM_CLIENT_ID |
Yes | From the Server-to-Server OAuth app |
clientSecret |
ZOOM_CLIENT_SECRET |
Yes | From the same app |
accountId |
ZOOM_ACCOUNT_ID |
Yes | Sent with message calls, not with the token request |
robotJid |
ZOOM_ROBOT_JID |
Yes | The Bot JID from Team Chat Subscription |
webhookSecretToken |
ZOOM_WEBHOOK_SECRET_TOKEN |
Yes | Signs webhooks and the setup challenge |
userName |
ZOOM_BOT_USERNAME |
No | Defaults to zoom-bot |
markdownSupport |
ZOOM_MARKDOWN_SUPPORT |
No | Defaults to true. Sends is_markdown_support: true and a Zoom Markdown body; false falls back to structured segments |
logger |
— | No | Defaults to a console logger at info, tagged zoom |
A missing credential throws a ValidationError the moment the adapter is
constructed, naming the variable to set. That is a setup problem surfacing
early, not a runtime bug — the app will not start until it is fixed.
Marketplace setup shows where each value lives in the Zoom dashboard.
Formatting mode
markdownSupport defaults to true, which sends is_markdown_support: true with
a Zoom Markdown body. That is the only mode that renders links, strikethrough,
block quotes, and images. In this mode Zoom ignores the structured style field,
so all emphasis has to live in the text.
Set it to false to send structured plain-text segments instead. That mode bolds
a paragraph only when the whole paragraph is bold, and flattens inline bold and
italic; links, strikethrough, quotes, and images keep their text but lose their
formatting. Formatting has the token-by-token comparison.
createZoomAdapter({ markdownSupport: false });
Logging
Pass your own logger to route adapter output into whatever you already use:
createZoomAdapter({ logger: myLogger.child("zoom") });
Debug level is where the useful detail goes — Zoom’s error response bodies, dropped events, and the body hex written when a signature comparison fails.
Authentication
The adapter fetches a Server-to-Server OAuth token with the
client_credentials grant and keeps it in memory, refreshing a minute before
it expires. A failed fetch throws an AuthenticationError carrying the HTTP
status.
The cache belongs to the adapter instance, so build one adapter and keep it — constructing a new one per request means a new token fetch every time.