Configuring Zoom
Dispatch ships with Zoom conference support. The Zoom plugin creates a conference call lasting up to 24 hours (Zoom's maximum) and generates a password to protect the call. The Web URL returned by the plugin to the messaging is created such that you need to click it and not worry about entering the password.
This plugin used to authenticate with a Zoom JWT app, a flow Zoom disabled on 2023-09-01. It now uses Server-to-Server OAuth, and the old API Key / API Secret fields are gone.
An existing Zoom plugin instance therefore carries configuration this version cannot read, and Dispatch treats unreadable plugin configuration as absent rather than raising. The plugin keeps showing as enabled, and every incident records "The Zoom plugin configuration could not be read…" on its timeline instead of getting a bridge. The old JWT key and secret cannot be converted and are useless against Zoom regardless.
To recover:
- Create a Server-to-Server OAuth app and configure the plugin as described below — once per project, in every organization. Plugin instances are per-project, so a deployment with five projects across two organizations needs ten.
- Re-enter every field, including Zoom API User Id. Because the stored configuration cannot be parsed, the edit form opens blank even for fields that did not change.
- Delete or deactivate the old JWT app in the Marketplace if one still exists.
Dispatch returns the stored client secret to the UI masked as **********, and saving the form writes back whatever it holds. Opening the Zoom plugin to change any other field — the API User Id, say — and saving will overwrite the client secret with those asterisks, and authentication then fails with Invalid client_id or client_secret. Re-enter the Client Secret every time you save this form. This affects every plugin with a secret field, not just Zoom.
Creating the Zoom app
-
In the Zoom App Marketplace, sign in as an account admin and choose Develop → Build App → Server-to-Server OAuth. (The JWT app type this plugin once required can no longer be created.)
-
Give the app a name, fill in the required basic information, and activate it.
-
Under Scopes, add the scopes for all four operations the plugin performs — create, read, update and delete a meeting.
Zoom offers two scope families, and which one you see depends on the app. Newly created apps are generally offered granular scopes only; the classic names may be unavailable:
Operation Classic Granular Create the meeting meeting:write:adminmeeting:write:meeting:adminRead the meeting meeting:read:adminmeeting:read:meeting:adminUpdate the invitee roster meeting:write:adminmeeting:update:meeting:adminDelete the meeting meeting:write:adminmeeting:delete:meeting:adminGranular scopes split what classic
meeting:write:admincovered into three. Selecting only the granular read and write pair is the common mistake: meetings are created successfully and then every roster update and deletion fails.A missing scope is not reported when the token is issued — the token is granted and the API call fails afterwards, so add the scopes before configuring Dispatch.
-
From App Credentials, copy the Account ID, Client ID and Client Secret.
Dispatch Configuration
The Zoom plugin is configured in the Dispatch Web UI under Settings -> Project -> Plugins. Add the plugin using the NEW button and then edit its configuration by clicking on the three vertical dots.
Enter the credentials through that form only. Do not pass them on a command line or place them in a URL — Dispatch stores the client secret in an encrypted column, and either of those would leak it to shell history, process listings or proxy logs.
Zoom API User Id
Email or user ID that meetings are created on behalf of.
Account ID
Account ID of the Server-to-Server OAuth app, from its App Credentials page.
Client ID
Client ID of the Server-to-Server OAuth app.
Client Secret
Client secret of the Server-to-Server OAuth app. Treat this as a credential — anyone holding it can act on the account through the app's scopes. Rotate it in the Marketplace if it is ever exposed.
Default Meeting Duration (Minutes)
Default duration in minutes for conference meetings. Defaults to 1440 minutes (1 day).
Validating the configuration
Create a test incident in a project with the plugin enabled and confirm a conference link appears. Any failure — authenticating, or the meeting call itself — is recorded on the incident timeline with the reason Zoom gave, and no conference is attached.
| Symptom | Cause |
|---|---|
HTTP 400: Invalid client_id or client_secret | Client ID or Client Secret is wrong, or the app was deactivated. Note Zoom answers the token endpoint with 400, not 401. |
HTTP 400: Invalid account_id (or a 400 naming the account) | The Account ID does not match the app's account. |
HTTP 400: grant type … is not supported | The token request used a grant Zoom does not accept here. Server-to-Server OAuth uses account_credentials. |
Zoom creation of the meeting failed with HTTP 400 and a message naming a scope | A required scope is missing from the app — most often an update or delete scope under the granular family. |
This API does not support client credentials for authorization | Returned by the API, not the token endpoint: a client_credentials token was presented to an endpoint requiring account authorization. |
The Zoom plugin configuration could not be read | The plugin still holds the retired API Key / API Secret. See the upgrade note above. |
Notes
- Access tokens live one hour and Zoom issues no refresh token. Dispatch acquires a token per operation and reuses it for the calls that operation makes; tokens are never written to disk. Nothing needs rotating on a schedule apart from the client secret itself, at whatever interval your policy requires.
- Zoom rate-limits meeting creation to 100 per day against the configured API user, and meeting updates to 100 per day per meeting. The first caps how many conference bridges a deployment can open in 24 hours account-wide; the second caps roster changes on a single long-running incident. Both surface as timeline errors rather than silent failures.
add_participantandremove_participantkeep the meeting'smeeting_inviteeslist in step with the incident's participants. This is roster metadata, not access control: the join URL works for anyone who holds it, so being added grants nothing beyond the published link, and being removed neither evicts anyone nor invalidates the link. Waiting-room behaviour is a separate concern this plugin does not manage.- Zoom replaces the invitee list wholesale, so Dispatch reads the meeting before each change and resends the existing invitees. A failure is recorded on the incident timeline and does not interrupt adding or removing the participant everywhere else.
- Zoom does report the invitee list on a read — verified against a real account, which is what closed issue #129 — so the roster is maintained normally: seeding two responders at creation and adding a third reads back as all three. If a read ever comes back without an invitee list, Dispatch leaves the roster alone instead of updating it, because rebuilding a wholesale-replaced list from a read that reported nothing would drop whoever was already on it. That is logged rather than written to the incident timeline — it is not a failure, and the founding responders are on the roster already. It should not happen; if it does, the log line names the shape Zoom answered with and is worth reporting. Nobody's ability to join changes either way.
- The meeting is created with an initial invitee roster: the responders resolved for the incident, minus anyone who has turned off Add me automatically to incident bridges in their own settings. It is sent inside the create request rather than added afterwards, so a roster Zoom rejects fails the create instead of leaving a meeting Dispatch has no record of. An incident with no such responders gets a meeting with no invitees, which is not an error, and the request Zoom receives is then exactly what it was before this roster existed.
- Zoom support has stated on the developer forum that
meeting_inviteesis consumed only by their calendar integrations, so an invitee — whether seeded at creation or added later — may never appear in the Zoom client. Dispatch sends what the API documents. Zoom's staff have also stated that populating this list does not generate email, so neither creating a bridge nor updating its roster notifies anyone.