Setup
1. Create Server-to-Server OAuth app
- https://marketplace.zoom.us > Develop > Build App
- App Type: Server-to-Server OAuth
- Copy Account ID, Client ID, Client Secret
2. Required scopes
meeting:read:adminmeeting:write:adminrecording:read:adminuser:read:admin
3. Event Subscriptions (webhook)
- App > Feature > Event Subscriptions
- Subscription URL:
https://[tenant].studeia.com/api/webhooks/video/zoom - Webhook secret token (copy to Studeia)
- Events: Meeting Started/Ended, Participant Joined/Left, Recording Completed, Recording Transcript Completed
4. Configure in Studeia
Settings > Video Provider > Zoom > Add: Account ID, Client ID, Client Secret, Webhook Secret Token, Set as default (optional). Studeia stores all AES-256-GCM encrypted.
How it works
LiveClass created with videoProvider=zoom → Studeia ZoomAdapter.createMeeting() with Server-to-Server OAuth token (cached Redis 55min) → POST /v2/users/{userId}/meetings → returns meetingId + joinUrl + startUrl. Student joins via joinMethod=external (Zoom doesn't support iframe).
Webhooks notify: meeting.started, participant.joined, meeting.ended, recording.completed, recording.transcript_completed.
Cron fallback
/api/cron/recording-sync every 15min as fallback (webhooks are unreliable — delays, subscription expiry, network failures).
RAG ingestion
After transcription approved by teacher: text becomes chunks + embeddings + ContentBlock with metadata { source: "live_class_transcript", liveClassId, courseId }. AI tutor cites: "In the live class on date X, the teacher explained that..."
Troubleshooting
- 401 Unauthorized: token expired. S2S OAuth token cached Redis 55min (auto-renew).
- Webhook not arriving: verify URL publicly accessible + HMAC signature.
- Recording without VTT: Zoom Cloud Recording must be enabled (not Local Recording).
Costs
Studeia: zero additional. Zoom: direct from Zoom (per-host license).