2.2 KiB
2.2 KiB
Webhooks - Verification
Verify webhook authenticity and handle URL validation.
Overview
Zoom provides two verification mechanisms:
- URL Validation - Verify your endpoint during setup
- Request Signature - Verify each webhook request
URL Validation
When you configure a webhook endpoint, Zoom sends a validation request:
{
"event": "endpoint.url_validation",
"payload": {
"plainToken": "random_token_string"
}
}
Response Required
Hash the token with your webhook secret and respond:
const crypto = require('crypto');
app.post('/webhook', (req, res) => {
const { event, payload } = req.body;
if (event === 'endpoint.url_validation') {
const hashForValidation = crypto
.createHmac('sha256', WEBHOOK_SECRET_TOKEN)
.update(payload.plainToken)
.digest('hex');
return res.json({
plainToken: payload.plainToken,
encryptedToken: hashForValidation
});
}
// Handle other events...
res.status(200).send();
});
Request Signature Verification
Verify each webhook request is from Zoom:
Headers
| Header | Description |
|---|---|
x-zm-signature |
Request signature |
x-zm-request-timestamp |
Request timestamp |
Verification Code
const crypto = require('crypto');
function verifyWebhook(req) {
const signature = req.headers['x-zm-signature'];
const timestamp = req.headers['x-zm-request-timestamp'];
// Prefer raw body bytes captured by your framework to avoid JSON re-serialization mismatches.
const body = req.rawBody ? req.rawBody.toString('utf8') : JSON.stringify(req.body);
const message = `v0:${timestamp}:${body}`;
const hash = crypto
.createHmac('sha256', WEBHOOK_SECRET_TOKEN)
.update(message)
.digest('hex');
const expectedSignature = `v0=${hash}`;
return signature === expectedSignature;
}
Security Best Practices
- Always verify signatures
- Check timestamp to prevent replay attacks
- Use HTTPS endpoints only
- Keep webhook secret secure
Resources
- Webhook verification: https://developers.zoom.us/docs/api/rest/webhook-reference/#verify-webhook-events