> ## Documentation Index
> Fetch the complete documentation index at: https://nango.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to set up webhooks with Zoom on Nango

> Learn how to receive real-time Zoom meeting and webinar events in your app using Nango webhooks

Zoom delivers webhooks through **Event Subscriptions**, configured once on your Marketplace app.

<Note>
  This guide covers the **Zoom** integration (authorization code, one end user per connection). For **Zoom (Server-to-Server OAuth)**, see its own [webhooks guide](/docs/api-integrations/zoom-cc/webhooks) instead — the setup differs enough between the two that they're documented separately.
</Note>

## How it works

1. Zoom sends a POST request to your Nango webhook URL when a subscribed event occurs on any account that authorized your app.
2. Nango verifies the delivery's signature against the **Webhook secret** on the integration, then matches the event's `payload.account_id` against `connection_config.accountId` on your connections — see [step 4](#4-nango-captures-your-zoom-account-id).
3. Nango forwards the event to your app, or triggers a sync.

<Note>
  Zoom's dashboard can't scope a multi-tenant app's Event Subscriptions to one account — the only scope that covers accounts other than your own, **"Only for users who have added this app,"** applies to every authorizing account collectively and delivers them all to the same single destination URL. There's no way to register a separate URL per account. This is why `account_id`-based matching exists: with one shared URL for every tenant, Zoom's payload itself is the only place a specific account is identified.
</Note>

<Note>
  Zoom doesn't expose Event Subscriptions setup through any API — there's no endpoint to create a subscription, enable an event type, or retrieve the Secret Token. Everything in [Setup](#setup) is a one-time dashboard task, done once per app.
</Note>

## Setup

### 1. Get your Nango webhook URL

In the Nango dashboard, open your Zoom integration and copy the **Webhook URL**. Use it as-is, with no query param — every account that authorizes your app sends events here, and Nango matches each one to a connection via `account_id`.

### 2. Set the webhook secret in Nango

1. Go to the [Zoom App Marketplace](https://marketplace.zoom.us/), open your app under **Develop → Build App**, then go to **Features → Access**.
2. Turn on **Event Subscriptions**. Zoom immediately generates a **Secret Token** on this page — copy it.
3. In the Nango dashboard, open your Zoom integration, go to the **Settings** tab, and paste it into the **Webhook Secret** field.

<Warning>
  Do this before creating any Event Subscription. Zoom validates the destination URL synchronously as part of both creating a subscription and saving the app, and Nango can only answer that validation request once the secret is set — see [step 3](#3-add-event-subscriptions-in-zoom).
</Warning>

### 3. Add Event Subscriptions in Zoom

1. On the same **Features → Access** page, click **Add Event Subscription**.
2. Under **Event notification endpoint URL**, paste your Nango webhook URL from [step 1](#1-get-your-nango-webhook-url).

   Zoom immediately sends a validation request to that URL to confirm you control it — Nango answers this automatically since the **Webhook secret** is already set. If validation fails, double-check the secret matches the **Secret Token** from [step 2](#2-set-the-webhook-secret-in-nango) before retrying.
3. Under the event subscription's scope, choose **"Only for users who have added this app"** — this is the scope that covers every account that connects through your app, not just your own.
4. Click **Add events** and select the events you want to receive.
5. Save the app.

This is a one-time setup — you don't need to repeat it as new accounts authorize your app.

### 4. Nango captures your Zoom account id

An authorization-code connection has no Zoom account id stored anywhere by default, so Nango can't match `payload.account_id` to one until it's saved. Nango does this for you automatically: right after a connection is created, a built-in step calls `GET /v2/users/me` and stores the result as `connection_config.accountId` on the connection. There's nothing to set up.

<Note>
  Connections created before this became the default have no `connection_config.accountId` and won't receive webhooks. Reconnect them (have the end user go through OAuth again) to trigger this step and start routing correctly — there's no API to backfill `connection_config` on an existing connection the way there is for metadata.
</Note>

<Warning>
  This step needs the `user:read:user` scope (or the legacy `user:read` scope on older apps) on your Zoom app. Without it, `GET /v2/users/me` fails silently, `connection_config.accountId` never gets set, and the connection won't receive webhooks — with no error surfaced anywhere. Add the scope when [registering your app](/docs/api-integrations/zoom/how-to-register-your-own-zoom-api-oauth-app).
</Warning>

## Handle the webhook

Once routed, you have two options:

* **Forward it to your app** — Nango forwards the event to your webhook URL with connection attribution. See [External webhook forwarding](/docs/guides/platform/webhook-forwarding).
* **Process it in a sync** — run a sync when the webhook arrives using `webhookSubscriptions` and `onWebhook` in a sync script. See [Real-time syncs](/docs/guides/functions/syncs/realtime-syncs).

## Payload

Every delivery has the same shape: an `event` name, an `event_ts` timestamp, and a `payload` object carrying `account_id` plus the affected `object` (its fields vary by event):

```json theme={null}
{
  "event": "meeting.started",
  "event_ts": 1626230691572,
  "payload": {
    "account_id": "Wk9PTV9BQ0NPVU5UX0lE",
    "object": {
      "id": "1234567890",
      "uuid": "Wk9PTV9NRUVUSU5HX0lE",
      "host_id": "Wk9PTV9VU0VSX0lE",
      "topic": "My Meeting",
      "type": 8,
      "start_time": "2021-07-13T21:44:51Z",
      "timezone": "America/Los_Angeles",
      "duration": 60
    }
  }
}
```

## Supported events

`event` (used as the webhook type for `webhookSubscriptions`/`onWebhook`) is one of Zoom's namespaced event names. Common ones:

| Event                        | Sent when                             |
| ---------------------------- | ------------------------------------- |
| `meeting.started`            | A meeting starts                      |
| `meeting.ended`              | A meeting ends                        |
| `meeting.participant_joined` | A participant joins a meeting         |
| `meeting.participant_left`   | A participant leaves a meeting        |
| `recording.completed`        | A cloud recording finishes processing |
| `webinar.started`            | A webinar starts                      |
| `webinar.ended`              | A webinar ends                        |

For the full list across meetings, webinars, recordings, phone, chat, and more, see [Zoom's webhook events reference](https://developers.zoom.us/docs/api/webhooks/#events).

## Connection matching

Nango matches `payload.account_id` against `connection_config.accountId` on your connections (populated in [step 4](#4-nango-captures-your-zoom-account-id)).

If nothing matches `payload.account_id`, the delivery is still accepted (so Zoom doesn't see an error and retry), but isn't routed to any connection.

<Warning>
  With no **Webhook secret** configured, every delivery is rejected outright — it never reaches routing.
</Warning>

## Retries and duplicate deliveries

Zoom retries a failed delivery (any non-2xx response) up to 3 times, at +5min, +25min, and +85min. Each attempt, including retries, carries a freshly signed timestamp — not a replay of the original request.

Nango also dedupes by the delivery's `x-zm-request-id`, which stays the same across retries. If the same delivery arrives again after already being processed, Nango accepts it (so Zoom stops retrying) but doesn't forward it or re-trigger a sync a second time.

## Rollback strategy

To stop deliveries, remove the event notification endpoint (or disable **Event Subscriptions** entirely) from **Features → Access** on your app in the [Zoom App Marketplace](https://marketplace.zoom.us/). Re-enable notifications by creating a new subscription with the steps above.

<Tip>Need help getting started? Join us in the [community](https://nango.dev/slack).</Tip>
