273 lines
7.2 KiB
Markdown
273 lines
7.2 KiB
Markdown
# Webhooks - Subscriptions
|
|
|
|
Configure webhook event subscriptions for real-time notifications.
|
|
|
|
## Overview
|
|
|
|
Subscribe to Zoom events to receive real-time notifications at your endpoint. This enables:
|
|
- Real-time meeting tracking (started, ended, participant changes)
|
|
- Automated recording processing pipelines
|
|
- User lifecycle management
|
|
- Skill chaining: Combine REST API calls with event-driven workflows
|
|
|
|
## Configuring Subscriptions
|
|
|
|
### Method 1: Via Marketplace Portal (Recommended for Setup)
|
|
|
|
1. Go to your app in [Marketplace](https://marketplace.zoom.us/)
|
|
2. Navigate to **Feature** → **Event Subscriptions**
|
|
3. Add subscription name and endpoint URL
|
|
4. Select events to subscribe to
|
|
5. Save and activate
|
|
|
|
### Method 2: Via API (Programmatic Management)
|
|
|
|
Use the Webhook Subscriptions API for programmatic subscription management.
|
|
|
|
#### Create Subscription
|
|
|
|
```bash
|
|
POST /webhooks/options
|
|
```
|
|
|
|
```json
|
|
{
|
|
"notification_endpoint_url": "https://your-server.com/zoom/webhook",
|
|
"events": [
|
|
"meeting.started",
|
|
"meeting.ended",
|
|
"meeting.participant_joined",
|
|
"recording.completed"
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Get Subscription
|
|
|
|
```bash
|
|
GET /webhooks/options
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"notification_endpoint_url": "https://your-server.com/zoom/webhook",
|
|
"events": [
|
|
"meeting.started",
|
|
"meeting.ended",
|
|
"meeting.participant_joined",
|
|
"recording.completed"
|
|
]
|
|
}
|
|
```
|
|
|
|
#### Update Subscription
|
|
|
|
```bash
|
|
PATCH /webhooks/options
|
|
```
|
|
|
|
```json
|
|
{
|
|
"events": [
|
|
"meeting.started",
|
|
"meeting.ended",
|
|
"recording.completed",
|
|
"user.created"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Subscription Settings
|
|
|
|
| Setting | Description | Required |
|
|
|---------|-------------|----------|
|
|
| **notification_endpoint_url** | Your HTTPS endpoint (must be publicly accessible) | Yes |
|
|
| **events** | Array of event types to subscribe to | Yes |
|
|
|
|
## Event Categories
|
|
|
|
### Meeting Events
|
|
| Event | Trigger |
|
|
|-------|---------|
|
|
| `meeting.created` | Meeting scheduled |
|
|
| `meeting.updated` | Meeting settings changed |
|
|
| `meeting.deleted` | Meeting deleted |
|
|
| `meeting.started` | Meeting begins |
|
|
| `meeting.ended` | Meeting ends |
|
|
| `meeting.participant_joined` | Participant joins |
|
|
| `meeting.participant_left` | Participant leaves |
|
|
| `meeting.sharing_started` | Screen share begins |
|
|
| `meeting.sharing_ended` | Screen share ends |
|
|
|
|
### Recording Events
|
|
| Event | Trigger |
|
|
|-------|---------|
|
|
| `recording.started` | Cloud recording begins |
|
|
| `recording.stopped` | Cloud recording paused/stopped |
|
|
| `recording.paused` | Cloud recording paused |
|
|
| `recording.resumed` | Cloud recording resumed |
|
|
| `recording.completed` | Recording processed and available |
|
|
| `recording.trashed` | Recording moved to trash |
|
|
| `recording.deleted` | Recording permanently deleted |
|
|
| `recording.recovered` | Recording restored from trash |
|
|
|
|
### User Events
|
|
| Event | Trigger |
|
|
|-------|---------|
|
|
| `user.created` | New user added |
|
|
| `user.updated` | User details changed |
|
|
| `user.deleted` | User removed |
|
|
| `user.deactivated` | User deactivated |
|
|
| `user.activated` | User activated |
|
|
|
|
### Webinar Events
|
|
| Event | Trigger |
|
|
|-------|---------|
|
|
| `webinar.created` | Webinar scheduled |
|
|
| `webinar.started` | Webinar begins |
|
|
| `webinar.ended` | Webinar ends |
|
|
| `webinar.registration_created` | New registration |
|
|
|
|
## Subscription Object Schema
|
|
|
|
```json
|
|
{
|
|
"notification_endpoint_url": "string (HTTPS URL)",
|
|
"events": ["array of event type strings"],
|
|
"secret_token": "string (for signature verification)"
|
|
}
|
|
```
|
|
|
|
## Code Examples
|
|
|
|
### JavaScript - Express Webhook Handler with Subscription Check
|
|
|
|
```javascript
|
|
const express = require('express');
|
|
const crypto = require('crypto');
|
|
const axios = require('axios');
|
|
|
|
const app = express();
|
|
app.use(express.json());
|
|
|
|
// Verify webhook signature
|
|
function verifyWebhookSignature(req, secret) {
|
|
const message = `v0:${req.headers['x-zm-request-timestamp']}:${JSON.stringify(req.body)}`;
|
|
const hashForVerify = crypto
|
|
.createHmac('sha256', secret)
|
|
.update(message)
|
|
.digest('hex');
|
|
|
|
const signature = `v0=${hashForVerify}`;
|
|
return signature === req.headers['x-zm-signature'];
|
|
}
|
|
|
|
// Handle webhook events
|
|
app.post('/zoom/webhook', (req, res) => {
|
|
const WEBHOOK_SECRET = process.env.ZOOM_WEBHOOK_SECRET;
|
|
|
|
if (!verifyWebhookSignature(req, WEBHOOK_SECRET)) {
|
|
return res.status(401).send('Unauthorized');
|
|
}
|
|
|
|
const { event, payload } = req.body;
|
|
|
|
switch (event) {
|
|
case 'meeting.started':
|
|
console.log(`Meeting started: ${payload.object.topic}`);
|
|
break;
|
|
case 'meeting.ended':
|
|
console.log(`Meeting ended: ${payload.object.uuid}`);
|
|
break;
|
|
case 'recording.completed':
|
|
processRecording(payload.object);
|
|
break;
|
|
}
|
|
|
|
res.status(200).send('OK');
|
|
});
|
|
```
|
|
|
|
### JavaScript - Manage Subscriptions via API
|
|
|
|
```javascript
|
|
async function updateWebhookSubscription(accessToken, events) {
|
|
const response = await axios.patch(
|
|
'https://api.zoom.us/v2/webhooks/options',
|
|
{ events },
|
|
{
|
|
headers: {
|
|
'Authorization': `Bearer ${accessToken}`,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
}
|
|
);
|
|
return response.data;
|
|
}
|
|
|
|
// Add new events to subscription
|
|
await updateWebhookSubscription(token, [
|
|
'meeting.started',
|
|
'meeting.ended',
|
|
'recording.completed',
|
|
'user.created', // New event
|
|
'user.deleted' // New event
|
|
]);
|
|
```
|
|
|
|
## Multiple Subscriptions
|
|
|
|
You can create multiple subscriptions to:
|
|
- Send different events to different endpoints
|
|
- Separate production and development endpoints
|
|
- Organize by event type
|
|
- Route events to different microservices
|
|
|
|
## Skill Chaining
|
|
|
|
Webhooks are commonly combined with REST API in skill chains:
|
|
|
|
| Chain | Pattern | Use Case |
|
|
|-------|---------|----------|
|
|
| REST API → Webhooks | Create meeting, subscribe to events | Track meeting lifecycle |
|
|
| Webhooks → REST API | Receive event, fetch details | Recording download on completion |
|
|
| Users API → Webhooks | Create user, subscribe to user events | User lifecycle tracking |
|
|
|
|
**Example: Meeting creation with event tracking**
|
|
```javascript
|
|
// Step 1: Subscribe to meeting events (one-time setup)
|
|
await updateWebhookSubscription(token, ['meeting.started', 'meeting.ended']);
|
|
|
|
// Step 2: Create meeting via REST API
|
|
const meeting = await createMeeting(token, { topic: 'Team Standup', type: 2 });
|
|
|
|
// Step 3: When meeting.started webhook arrives, track it
|
|
// Step 4: When meeting.ended webhook arrives, process attendance
|
|
```
|
|
|
|
See [meeting-details-with-events.md](../../general/use-cases/meeting-details-with-events.md) for complete skill chaining patterns.
|
|
|
|
## Testing Webhooks
|
|
|
|
1. **Local development**: Use [ngrok](https://ngrok.com/) to expose local endpoint
|
|
```bash
|
|
ngrok http 3000
|
|
```
|
|
2. **Webhook logs**: Check Marketplace portal → App → Webhooks → Logs
|
|
3. **Test endpoint**: Validate signature handling before going live
|
|
4. **Retry behavior**: Zoom retries failed webhooks (5xx responses) up to 3 times
|
|
|
|
## Required Scopes
|
|
|
|
| Scope | Operations |
|
|
|-------|------------|
|
|
| `webhook:read:admin` | View webhook settings |
|
|
| `webhook:write:admin` | Modify webhook settings |
|
|
|
|
## Resources
|
|
|
|
- **Webhooks overview**: https://developers.zoom.us/docs/api/rest/webhook-reference/
|
|
- **Event types**: https://developers.zoom.us/docs/api/rest/reference/zoom-api/events/
|
|
- **Signature verification**: See [signature-verification.md](signature-verification.md)
|