
Build Zoom Meeting Sdk App
- 1.4k installs
- 23.1k repo stars
- Updated July 28, 2026
- anthropics/knowledge-work-plugins
build-zoom-meeting-sdk-app is an agent skill for reference skill for zoom meeting sdk. use after routing to a meeting-embed workflow when implementing real zoom meeting joins, platform-specific sdk behavior, auth and joi
About
The build-zoom-meeting-sdk-app skill is designed for reference skill for Zoom Meeting SDK. Use after routing to a meeting-embed workflow when implementing real Zoom meeting joins, platform-specific SDK behavior, auth and join. /build-zoom-meeting-sdk-app Background reference for embedded Zoom meetings across web, mobile, desktop, and Linux bot environments. Prefer build-zoom-meeting-app or build-zoom-bot first, then route here for platform detail. Invoke when the user asks about build zoom meeting sdk app or related SKILL.md workflows.
- If the user asks to embed/join meetings inside their app UI, route to Meeting SDK implementation.
- Zoom app with Meeting SDK credentials.
- SDK Key and Secret from Marketplace.
- Platform-specific development environment (Web, Android, iOS, macOS, Unreal, Electron, Linux, or Windows).
- android/SKILL.md - Android SDK (default/custom UI, join/start/auth lifecycle, mobile integration).
Build Zoom Meeting Sdk App by the numbers
- 1,421 all-time installs (skills.sh)
- +82 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #251 of 1,896 Design & UI/UX skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
build-zoom-meeting-sdk-app capabilities & compatibility
- Capabilities
- if the user asks to embed/join meetings inside t · zoom app with meeting sdk credentials · sdk key and secret from marketplace · platform specific development environment (web,
- Use cases
- frontend
What build-zoom-meeting-sdk-app says it does
Reference skill for Zoom Meeting SDK. Use after routing to a meeting-embed workflow when implementing real Zoom meeting joins, platform-specific SDK behavior, auth and join flows,
Reference skill for Zoom Meeting SDK. Use after routing to a meeting-embed workflow when implementing real Zoom meeting joins, platform-specific SDK behavior, a
npx skills add https://github.com/anthropics/knowledge-work-plugins --skill build-zoom-meeting-sdk-appAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.4k |
|---|---|
| repo stars | ★ 23.1k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 28, 2026 |
| Repository | anthropics/knowledge-work-plugins ↗ |
How do I reference skill for zoom meeting sdk. use after routing to a meeting-embed workflow when implementing real zoom meeting joins, platform-specific sdk behavior, auth and join?
Reference skill for Zoom Meeting SDK. Use after routing to a meeting-embed workflow when implementing real Zoom meeting joins, platform-specific SDK behavior, auth and join.
Who is it for?
Developers using build zoom meeting sdk app workflows documented in SKILL.md.
Skip if: Skip when the task falls outside build-zoom-meeting-sdk-app scope or needs a different stack.
When should I use this skill?
User asks about build zoom meeting sdk app or related SKILL.md workflows.
What you get
Completed build-zoom-meeting-sdk-app workflow with documented commands, files, and expected deliverables.
- SDK integration code
- auth and join flows
- UI module setup
By the numbers
- Local package zoom-sdk-android-6.7.5.37500 with mobilertc.aar and sample apps
- Docs validation covers get-started, integrate, default-ui, custom-ui, and error-codes paths
Files
Zoom Meeting SDK (Android)
Use this skill when building Android apps with embedded Zoom meeting capabilities.
Start Here
1. android.md 2. concepts/lifecycle-workflow.md 3. concepts/architecture.md 4. examples/join-start-pattern.md 5. scenarios/high-level-scenarios.md 6. references/android-reference-map.md 7. references/environment-variables.md 8. references/versioning-and-compatibility.md 9. troubleshooting/common-issues.md
Routing Notes
- Use default UI first for first successful join/start validation.
- Move to custom UI once auth, meeting state transitions, and permissions are stable.
- For signature/JWT mistakes, chain with ../../oauth/SKILL.md and ../references/signature-playbook.md.
Key Sources
- Docs: https://developers.zoom.us/docs/meeting-sdk/android/
- API reference: https://marketplacefront.zoom.us/sdk/meeting/android/index.html
- Broader guide: ../SKILL.md
Operations
- RUNBOOK.md - 5-minute preflight and debugging checklist.
Meeting SDK Android Guide
Scope
Android Meeting SDK integration for default UI, custom UI, auth, start/join, and in-meeting feature modules.
Validation Snapshot
- Crawled docs path includes:
get-started,integrate,start-join-mtg-webinar,default-ui,custom-ui,resource/error-codes. - API reference snapshot includes class/interface/function maps from
index.html,annotated.html,classes.html,files.html, andfunctions*pages. - Local package checked:
zoom-sdk-android-6.7.5.37500(containsmobilertc.aar, sample apps, dynamic sample modules).
Practical Guidance
1. Initialize and authenticate SDK. 2. Get first successful join in default UI. 3. Add feature flags/settings and error handling. 4. Move to custom UI only for required UX control. 5. Add observability for meeting status and SDK callback failures.
Android Architecture
Layer Model
- App UI layer (Activity/Fragment/Compose wrapper).
- Meeting orchestration layer (init/auth/join/start state machine).
- SDK facade layer (
mobilertc.aarinterfaces and controllers). - Backend signing service (short-lived signature/JWT issuance).
Reference Flow
Android UI -> App Meeting Service -> Backend Signature API -> Zoom Meeting SDK
^ | | |
| v v v
User actions Session state store Token/role policy Meeting callbacksWhy this split
- Keeps SDK secret server-side.
- Prevents UI from owning auth/security logic.
- Enables deterministic retry policy on join/start failures.
Android Lifecycle Workflow
Core Sequence
1. App boot + permissions precheck (camera, mic, notifications as needed). 2. SDK initialize (InitSDK-style flow in Android layer). 3. Auth for SDK access (non-login/API user paths or signed flow). 4. Join/start meeting request. 5. In-meeting event handling (audio/video/chat/share/BO/recording where enabled). 6. Leave/end meeting and release/cleanup.
Failure Domains
- Initialization errors (SDK state, app config, package conflicts).
- Auth/signature mismatches (expired token, role mismatch, malformed payload).
- Join/start parameter mismatch (meeting number, passcode, meeting type).
- Device/media state mismatch (permissions, audio route, camera availability).
Android Join/Start Pattern
Join (attendee)
1. Backend creates short-lived SDK signature/JWT. 2. App initializes SDK and verifies init callback success. 3. App executes join with normalized meeting number + passcode. 4. App subscribes to meeting status callbacks before join call returns.
Start (host)
1. Backend resolves host token (ZAK) + role-aware signature. 2. App executes start flow and validates host privilege errors explicitly. 3. App applies host-only features conditionally (recording, management controls).
Guardrails
- Do not hardcode secret in app.
- Normalize meeting identifiers as strings of digits.
- Treat SDK callback thread behavior as asynchronous; avoid UI blocking.
Android Reference Map
Sources
- Docs: https://developers.zoom.us/docs/meeting-sdk/android/
- API Reference: https://marketplacefront.zoom.us/sdk/meeting/android/index.html
Crawl Coverage Snapshot
- Docs pages captured:
70 - API reference pages captured:
1003
Key API Entry Pages
index.mdannotated.mdclasses.mdfiles.mdhierarchy.mdfunctions.mdandfunctions_*functions_func_*functions_vars_*
Notable API Surface Areas
- Meeting lifecycle and service controllers
- Audio/video/share controllers
- Breakout room and webinar interfaces
- AI Companion / smart summary interfaces
- Raw data helpers and delegates
Drift Signals to Watch
- Newly added
AI Companion,smart summary, andavatarinterfaces. - Legacy helper names retained with new parallel interfaces.
Android Meeting SDK Environment Variables
| Variable | Required | Purpose | Where to find |
|---|---|---|---|
ZOOM_SDK_KEY | Yes | SDK signing identity | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_SDK_SECRET | Yes | Server-side signing secret | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_MEETING_NUMBER | Join/start | Meeting identifier | Zoom invite / web portal / Meetings API |
ZOOM_MEETING_PASSWORD | Conditional | Meeting passcode | Zoom invite details / Meetings API |
ZOOM_ROLE | Yes | Signature role (0 attendee, 1 host) | App business logic |
ZOOM_ZAK | Host start | Host authorization token | Zoom REST API token flow |
Notes
- Generate signatures server-side only.
- Keep mobile client as consumer of short-lived tokens, not secret holder.
Android Versioning and Compatibility
Observed Versions
- Local SDK package:
v6.7.5.37500 - Docs baseline: current Meeting SDK Android docs tree captured on this crawl.
Compatibility Practices
- Pin exact SDK artifact version in Gradle.
- Track enum/interface additions between releases.
- Validate custom UI flows after each SDK update (higher break risk vs default UI).
Contradiction/Drift Notes
- Docs contain legacy and current path variants (
add-featuresvscustom-ui/default-uisections). - Some page titles include suffix artifacts (for example
| MSDK | And), indicating doc-generation inconsistencies, not functional API differences.
Meeting SDK Android 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for Android (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/android/
- https://marketplacefront.zoom.us/sdk/meeting/android/index.html
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/android/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/android/
Android High-Level Scenarios
Scenario 1: Field-service meeting embed
- Technician app embeds meeting in default UI.
- Uses attendee join tokens from backend.
- Adds device telemetry + service-quality diagnostics.
Scenario 2: Branded healthcare consult
- Start from default UI for reliability.
- Phase in custom UI for constrained controls and guided workflows.
- Enforce strict permission and foreground-service handling.
Scenario 3: Training/coaching session app
- Uses breakout room and in-meeting chat flows.
- Tracks lifecycle events for attendance and session analytics.
- Uses migration-safe handling for renamed SDK enums across upgrades.
Android Common Issues
1. Init succeeds, join fails immediately
- Verify signature freshness and role correctness.
- Confirm meeting number/passcode are exact values.
- Confirm auth path aligns with join/start path (attendee vs host).
2. Works in sample, fails in app
- Compare manifest permissions and ProGuard/R8 rules.
- Compare Gradle dependency graph for collisions.
- Confirm lifecycle ownership (Activity recreation handling).
3. Custom UI instability
- Validate default UI parity first.
- Re-check event registration ordering before rendering operations.
- Guard against null/late user stream references.
4. Version drift breakage
- Rebuild against pinned SDK version.
- Revisit renamed interfaces in API reference map.
- Re-test breakout, raw data, and advanced feature modules after upgrade.
High-Level Scenarios
1. Desktop internal meeting client
- User signs in to your app.
- Backend mints SDK JWT.
- Electron app embeds join/start for scheduled meetings.
- Controllers handle mute/video/chat/share.
2. Compliance-focused recorder assistant
- Controlled join flow for operator accounts.
- Recording and raw data modules capture meeting artifacts.
- Data moves to internal compliance pipeline.
3. Support operations dashboard
- Agents join support sessions from desktop app.
- Use participants/chat/share modules for assistance workflows.
- Waiting room and host control automation for queue handling.
4. AI-assisted desktop meeting copilot
- Meeting join via Electron SDK.
- Raw data or AI-related modules feed local/remote AI services.
- Live summary/action extraction in side panel.
Lifecycle Workflow
Recommended runtime sequence for Electron Meeting SDK integrations:
1. Initialize SDK wrapper (zoom_sdk level init). 2. Authenticate with SDK JWT through auth service. 3. Configure meeting parameters and settings controllers. 4. Join or start meeting through meeting service. 5. Bind meeting controllers/events (audio, video, participants, chat, share). 6. Optional raw data and advanced modules. 7. Leave meeting and release SDK resources cleanly.
Sequence Diagram
Electron App
-> initSDK
-> authWithJwt
-> create/get meeting service
-> joinMeeting/startMeeting
-> subscribe callbacks
-> apply controller actions
-> leaveMeeting
-> cleanupWhy this order matters
- Controller operations before successful auth or join usually fail or no-op.
- Settings should be applied before meeting join where possible.
- Cleanup prevents stale state and callback leaks on app relaunch.
SDK Architecture Pattern
Electron wrapper follows a service + controller + event callback model.
Core layers
zoom_sdkbootstrap/auth wrappers.- Meeting service facade.
- Feature controllers (audio, video, participants, recording, share, chat, etc.).
- Settings service/controllers.
- Optional modules (raw data, webinar, AI companion, whiteboard, QA/polling).
Universal pattern
1. Get service/controller. 2. Register event callback(s). 3. Invoke async action. 4. Handle callback result/error codes.
Implementation guidance
- Centralize callback routing in one internal event bus.
- Use typed wrapper methods per module to reduce invocation mistakes.
- Log SDK return codes consistently for diagnostics.
Authentication Pattern
Backend
- Accept meeting context inputs.
- Generate short-lived Meeting SDK JWT.
- Return token to authenticated Electron client session.
Electron app
1. Initialize SDK. 2. Send SDK JWT to auth module. 3. Wait for auth callback success. 4. Continue to meeting join/start.
Guardrails
- Refresh token on expiry windows.
- Fail fast on auth callback errors and show actionable logs.
- Do not persist SDK secret or signing logic in Electron bundle.
Join Meeting Pattern
Flow
1. Collect meeting number, display name, passcode/credential strategy. 2. Ensure SDK auth completed. 3. Call join/start meeting API via meeting service. 4. Wait for in-meeting callbacks. 5. Initialize required controllers (audio/video/chat/share/participants).
Operational checks
- Validate meeting number format before SDK call.
- Normalize role-specific fields for attendee vs host start flows.
- Apply settings defaults (audio/video/share) before join when supported.
Raw Data Pattern
Raw data flows are advanced and require hardening.
Typical use cases
- Local AI processing.
- Quality monitoring.
- Compliance capture.
Pattern
1. Enable raw data module after meeting join. 2. Subscribe to relevant streams. 3. Transfer frames/samples through controlled IPC/data path. 4. Apply backpressure, buffering, and clean shutdown handling.
Risks
- Performance overhead if frame handling is not bounded.
- Sensitive data exposure if raw buffers are not protected.
- Version mismatch risks in native addon dependencies.
Setup Guide
This guide focuses on integration structure, not one-click install scripts.
Prerequisites
- Electron + Node toolchain compatible with your chosen SDK package.
- Native build toolchain for Node addon compilation.
- Backend endpoint for SDK JWT signing.
Minimal setup checklist
1. Add Meeting SDK Electron package and native artifacts. 2. Wire preload/main process APIs for SDK invocation. 3. Implement secure backend endpoint for JWT generation. 4. Add app-level init/auth/join lifecycle handlers. 5. Add structured logging around SDK callbacks and error codes.
Security baseline
- Keep SDK secret off client.
- Gate any raw data transport with encryption and access controls.
- Validate all IPC boundaries between renderer and main process.
Electron API Reference
Primary crawled references:
raw-docs/developers.zoom.us/docs/meeting-sdk/electron.mdraw-docs/developers.zoom.us/docs/meeting-sdk/electron/download-and-install.mdraw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/electron/index.md
Reference set includes module docs for:
- auth and SDK bootstrap
- meeting service and feature controllers
- settings controllers
- raw data and advanced modules
- generated JS wrapper documentation
Use Module Map to navigate quickly by feature area.
Module Map
Bootstrap and auth
module-zoom_electron_sdk.mdmodule-zoom_auth.mdmodule-zoom_meeting.md
Core meeting controls
module-zoom_meeting_audio.mdmodule-zoom_meeting_video.mdmodule-zoom_meeting_share.mdmodule-zoom_meeting_participants_ctrl.mdmodule-zoom_meeting_chat.mdmodule-zoom_meeting_recording.md
Settings
module-zoom_setting.mdmodule-zoom_setting_audio.mdmodule-zoom_setting_video.mdmodule-zoom_setting_share.mdmodule-zoom_setting_recording.md
Advanced/optional
module-zoom_rawdata.mdmodule-zoom_meeting_webinar.mdmodule-zoom_meeting_ai_companion.mdmodule-zoom_meeting_whiteboard.mdmodule-zoom_meeting_polling.mdmodule-zoom_meeting_qa.md
Meeting SDK Electron 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for Electron (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/electron/
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/electron/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/electron/
Common Issues
SDK initializes but join fails
- Verify auth callback success before join.
- Validate meeting number/passcode format.
- Confirm role and host-specific fields for start flow.
Native addon load failures
- Check Electron/Node ABI compatibility.
- Rebuild native modules for your Electron version.
- Confirm platform binaries are present in package/runtime paths.
Callback silence or partial feature behavior
- Ensure controller callbacks are registered before invoking actions.
- Avoid duplicate singleton initialization in multiple processes.
- Confirm feature availability for account type/meeting type.
Raw data instability
- Apply bounded queues and worker separation.
- Reduce frame sampling rates if renderer/main process saturates.
- Ensure graceful unsubscription on leave/cleanup.
Deprecated and Contradictions
Observed from crawled docs and provided SDK package context:
1. Electron version guidance contradiction
- Package README mentions installing Electron
33.0.0. - Same README also says sample app currently does not support Electron 10 or above.
Action: treat sample README version text as inconsistent and validate against official current compatibility guidance before rollout.
2. Deprecated module flags in API reference
Crawled API reference marks deprecations in several areas, including webinar and some setting-related modules.
Action: avoid new dependencies on deprecated modules; isolate behind adapter interfaces if legacy support is required.
3. Feature availability caveats
Multiple module docs include "not supported" notes depending on platform/account/meeting context.
Action: gate feature use with runtime capability checks and fail gracefully.
4. Python/build toolchain caveat in sample notes
Sample package notes mention build issues with newer Python (distutils-related) and suggest older Python versions.
Action: pin build environment versions in CI and document exact toolchain used for your release branch.
Version Drift
Electron SDK integrations are sensitive to dependency drift.
Drift vectors
- Electron runtime upgrades
- Node ABI changes
- Native addon compiler toolchain changes
- Meeting SDK wrapper package updates
Control strategy
1. Pin tested Electron + SDK versions. 2. Keep a compatibility matrix in your project docs. 3. Rebuild and smoke test on every runtime change. 4. Run callback/error-code regression tests for join/start/audio/video/share.
iOS Architecture
Layer Model
- UIKit/SwiftUI app layer.
- Meeting orchestration layer (state machine + delegate fan-out).
- SDK adapter layer (MobileRTC service wrappers).
- Backend signature/token service.
Reference Flow
iOS UI -> Meeting Coordinator -> Backend Sign Service -> Meeting SDK
^ | | |
| v v v
User intents Local state store Role/token policy SDK delegates/eventsWhy this split
- Keeps security-critical logic server-side.
- Stabilizes delegate/event ordering.
- Makes upgrade drift easier to isolate.
iOS Lifecycle Workflow
Core Sequence
1. App launch and permission strategy (camera/mic, optional share path prep). 2. SDK initialization and auth callbacks. 3. Join/start decision branch. 4. In-meeting event wiring (audio/video/chat/share/webinar where needed). 5. Background/foreground transitions and interruption recovery. 6. Leave/end and cleanup.
Failure Domains
- Expired or mismatched signature data.
- Role/host-token mismatch in start flow.
- App lifecycle interruption (phone call/audio route/background).
- Incomplete delegate registration before meeting transitions.
iOS Join/Start Pattern
Join (attendee)
1. Request short-lived signature from backend. 2. Initialize/auth SDK and verify callback success. 3. Call join with meeting number/passcode/display name. 4. Observe meeting status and user/video delegate events.
Start (host)
1. Backend provides host ZAK + role-aware signature. 2. Call start path with host token. 3. Validate host-only feature permissions before showing controls.
Guardrails
- Do not put SDK secret in iOS app bundle.
- Register delegates before initiating meeting transitions.
- Persist minimal state needed for background recovery.
Meeting SDK iOS Guide
Scope
iOS Meeting SDK integration for init/auth, default/custom UI, meeting join/start, and in-meeting features.
Validation Snapshot
- Docs coverage includes: setup/get-started, default UI and custom UI feature tracks, PKCE/start/join/auth, FAQ/error-code pages.
- API reference snapshot includes class/protocol maps, file references, and member lists.
- Local package checked:
zoom-sdk-ios-6.7.5.33005withMobileRTCSampleand Objective-C sample presenters.
Practical Guidance
1. Achieve stable default UI join path first. 2. Add host-start and advanced features after baseline is stable. 3. Move to custom UI only where UX requires it. 4. Add explicit permission and audio route diagnostics.
iOS Meeting SDK Environment Variables
| Variable | Required | Purpose | Where to find |
|---|---|---|---|
ZOOM_SDK_KEY | Yes | SDK signing identity | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_SDK_SECRET | Yes | Server-side signing secret | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_MEETING_NUMBER | Join/start | Meeting identifier | Zoom invite / web portal / Meetings API |
ZOOM_MEETING_PASSWORD | Conditional | Meeting passcode | Zoom invite details / Meetings API |
ZOOM_ROLE | Yes | Signature role (0 attendee, 1 host) | App business logic |
ZOOM_ZAK | Host start | Host authorization token | Zoom REST API token flow |
Notes
- Keep secrets server-side only.
- Consider storing only short-lived meeting tokens in app memory.
iOS Reference Map
Sources
- Docs: https://developers.zoom.us/docs/meeting-sdk/ios/
- API Reference: https://marketplacefront.zoom.us/sdk/meeting/ios/annotated.html
Crawl Coverage Snapshot
- Docs pages captured:
55 - API reference pages captured:
643
Key API Entry Pages
annotated.mdclasses.mdfiles.mdhierarchy.mdfunctions*globals*pages.md
Notable API Surface Areas
MobileRTCMeetingServicecategory extensions- Protocol-heavy delegate architecture
- Audio/video/share/raw-data helpers
- BO/webinar/AI companion interfaces
Drift Signals to Watch
- New AI Companion and smart summary handlers.
- Category-level method movement between releases.
iOS Versioning and Compatibility
Observed Versions
- Local SDK package:
v6.7.5.33005 - Docs baseline: current iOS Meeting SDK docs tree captured on this crawl.
Compatibility Practices
- Pin exact SDK package release.
- Re-verify category/protocol method availability on upgrades.
- Re-test background/audio interruption flows every upgrade.
Contradiction/Drift Notes
- Package
README.mdpoints to generic Meeting SDK docs root, while platform docs live at/meeting-sdk/ios/. - Package changelog file only links externally; keep your own integration delta notes per release.
Meeting SDK iOS 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for iOS (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/ios/
- https://marketplacefront.zoom.us/sdk/meeting/ios/annotated.html
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/ios/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/ios/
iOS High-Level Scenarios
Scenario 1: Telehealth client app
- Attendee join with strict permission prompts.
- Uses waiting-room and status callbacks for guided UX.
- Records app-level diagnostic events for support triage.
Scenario 2: Internal host app
- Uses host start flow with
ZAK. - Enables moderator tools only after host privilege confirmation.
- Applies policy checks for recording and participant management.
Scenario 3: Branded custom in-meeting experience
- Starts from default UI parity baseline.
- Adds custom UI modules for selected controls/views.
- Maintains fallback path when advanced features regress after SDK update.
iOS Common Issues
1. Join/start fails with generic error
- Validate signature expiry and role.
- Confirm meeting number/passcode mapping.
- Confirm host flow has valid
ZAK.
2. Delegate callbacks missing or late
- Register delegates before invoking join/start.
- Verify lifecycle transitions do not deallocate coordinator/service objects.
3. Audio route or interruption issues
- Handle route changes explicitly.
- Re-sync meeting media state after interruption/background return.
4. Upgrade regressions
- Compare protocol/category signatures against prior version.
- Re-test custom UI extensions first; they are usually most drift-prone.
High-Level Scenarios for Meeting SDK Linux
This guide covers common bot architectures and patterns that remain stable across SDK versions, with flexible approaches to handle API changes.
Scenario 1: Transcription Bot
Goal: Join meetings to transcribe conversations in real-time.
Architecture
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Meeting │───►│ Meeting SDK │───►│ Audio Stream │───►│ Transcription│
│ Starts │ │ Join + Auth │ │ (PCM 32kHz) │ │ Service │
└─────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ StartRaw │ │ onMixedAudio │ │ Store │
│ Recording │ │ RawDataReceived │ │ Transcript │
└──────────────┘ └─────────────────┘ └──────────────┘Core Pattern (Version-Flexible)
// STEP 1: Initialize (stable across versions)
InitParam init_params;
init_params.strWebDomain = "https://zoom.us";
init_params.enableLogByDefault = true;
init_params.rawdataOpts.audioRawDataMemoryMode = ZoomSDKRawDataMemoryModeHeap;
InitSDK(init_params);
// STEP 2: Authenticate (JWT token - stable)
AuthContext auth_ctx;
auth_ctx.jwt_token = getJWTToken(); // Generate from SDK credentials
CreateAuthService(&auth_service);
auth_service->SDKAuth(auth_ctx);
// STEP 3: Join meeting (flexible - check SDK version for exact field names)
JoinParam join_param;
join_param.userType = SDK_UT_WITHOUT_LOGIN;
auto& params = join_param.param.withoutloginuserJoin;
params.meetingNumber = meeting_number;
params.userName = "Transcription Bot";
params.psw = meeting_password;
// Version-flexible: Check if these fields exist in your SDK version
// params.app_privilege_token = obf_token; // v5.15+
// params.onBehalfToken = obf_token; // Some versions
// params.userZAK = zak_token; // Alternative
meeting_service->Join(join_param);
// STEP 4: In onMeetingStatusChanged callback
if (status == MEETING_STATUS_INMEETING) {
// Get recording controller (pattern stable)
auto* record_ctrl = meeting_service->GetMeetingRecordingController();
// Start raw recording (enables raw data access)
if (record_ctrl->CanStartRawRecording() == SDKERR_SUCCESS) {
record_ctrl->StartRawRecording();
}
// Subscribe to audio
auto* audio_helper = GetAudioRawdataHelper();
audio_helper->subscribe(new TranscriptionAudioDelegate());
}
// STEP 5: Process audio
class TranscriptionAudioDelegate : public IZoomSDKAudioRawDataDelegate {
void onMixedAudioRawDataReceived(AudioRawData* data) override {
// Send PCM data to transcription service
sendToTranscriptionAPI(data->GetBuffer(), data->GetBufferLen());
}
};Version Handling Strategy
Flexible field access:
// Define a macro to check field existence at compile time
#ifdef HAS_APP_PRIVILEGE_TOKEN
params.app_privilege_token = token;
#elif defined(HAS_ON_BEHALF_TOKEN)
params.onBehalfToken = token;
#else
params.userZAK = token;
#endifRuntime capability detection:
SDKError canRecord = record_ctrl->CanStartRawRecording();
if (canRecord != SDKERR_SUCCESS) {
// Fallback: Try local recording
if (record_ctrl->CanStartRecording(true) == SDKERR_SUCCESS) {
record_ctrl->StartRecording(time, path);
}
}Key Resilience Patterns
1. Always check CanXXX() before calling XXX() 2. Have fallback authentication methods (OBF → ZAK → password-only) 3. Detect capabilities, don't assume 4. Use error codes to adapt behavior
---
Scenario 2: Recording Bot (Video + Audio)
Goal: Record meetings with synchronized audio and video.
Architecture
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Join Meeting │───►│ StartRawRecording│───►│ Subscribe │
│ │ │ │ │ Audio+Video │
└──────────────┘ └─────────────────┘ └──────────────┘
│
┌──────────────────────────┴─────────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Write YUV420 │ │ Write PCM │
│ video.yuv │ │ audio.pcm │
└──────────────┘ └──────────────┘
│ │
└──────────────────────────────────────────┬─┘
▼
┌──────────────┐
│ FFmpeg Merge │
│ output.mp4 │
└──────────────┘Core Pattern
// After joining and starting raw recording...
// Video subscription
class VideoRecorderDelegate : public IZoomSDKRendererDelegate {
std::ofstream videoFile;
void onRawDataFrameReceived(YUVRawDataI420* data) override {
int width = data->GetStreamWidth();
int height = data->GetStreamHeight();
// YUV420 format: Y + U + V planes (contiguous)
videoFile.write(data->GetYBuffer(), width * height);
videoFile.write(data->GetUBuffer(), (width/2) * (height/2));
videoFile.write(data->GetVBuffer(), (width/2) * (height/2));
}
};
IZoomSDKRenderer* video_renderer;
createRenderer(&video_renderer, new VideoRecorderDelegate());
video_renderer->setRawDataResolution(ZoomSDKResolution_720P);
video_renderer->subscribe(user_id, RAW_DATA_TYPE_VIDEO);
// Audio subscription
class AudioRecorderDelegate : public IZoomSDKAudioRawDataDelegate {
std::ofstream audioFile;
void onMixedAudioRawDataReceived(AudioRawData* data) override {
audioFile.write((char*)data->GetBuffer(), data->GetBufferLen());
}
};
auto* audio_helper = GetAudioRawdataHelper();
audio_helper->subscribe(new AudioRecorderDelegate());
// Post-processing with FFmpeg
system("ffmpeg -video_size 1280x720 -pixel_format yuv420p -f rawvideo -i video.yuv "
"-f s16le -ar 32000 -ac 1 -i audio.pcm "
"-c:v libx264 -c:a aac -shortest output.mp4");Handling Resolution Changes
Flexible resolution selection (adapt to SDK version):
// Try highest resolution available, fall back gracefully
ZoomSDKResolution resolutions[] = {
ZoomSDKResolution_1080P,
ZoomSDKResolution_720P,
ZoomSDKResolution_360P
};
for (auto res : resolutions) {
SDKError err = video_renderer->setRawDataResolution(res);
if (err == SDKERR_SUCCESS) {
std::cout << "Using resolution: " << res << std::endl;
break;
}
}---
Scenario 3: AI Meeting Assistant
Goal: Real-time meeting analysis with AI (sentiment, action items, summaries).
Architecture
┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────────┐
│ Audio Stream │───►│ Transcription │───►│ AI Analysis │───►│ Actions │
│ (Real-time) │ │ (AssemblyAI) │ │ (Anthropic) │ │ Identified │
└──────────────┘ └─────────────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Speaker │ │ Sentiment │ │ Generate │
│ Diarization │ │ Analysis │ │ Summary │
└─────────────────┘ └─────────────────┘ └──────────────┘Core Pattern
// Real-time streaming transcription
class AIAssistantAudioDelegate : public IZoomSDKAudioRawDataDelegate {
WebSocketClient transcription_ws; // AssemblyAI WebSocket
AIClient ai_client; // Anthropic/OpenAI
void onMixedAudioRawDataReceived(AudioRawData* data) override {
// Stream audio to real-time transcription
transcription_ws.send_audio(data->GetBuffer(), data->GetBufferLen());
}
};
// Process transcription results
void onTranscriptReceived(const std::string& text, const std::string& speaker) {
// Accumulate context
meeting_context += speaker + ": " + text + "\n";
// Every 30 seconds, analyze with AI
if (should_analyze()) {
std::string analysis = ai_client.analyze(meeting_context, {
"extract_action_items",
"detect_decisions",
"identify_blockers"
});
// Store results
meeting_insights.push_back(analysis);
}
}
// Post-meeting summary
void onMeetingEnded() {
std::string summary = ai_client.summarize(meeting_context, meeting_insights);
save_to_database(meeting_id, summary);
send_email_summary(participants, summary);
}Version Flexibility: Chat Integration
Some SDK versions support in-meeting chat:
// Try to send AI insights to meeting chat
auto* chat_ctrl = meeting_service->GetMeetingChatController();
if (chat_ctrl) {
// Check if available
if (chat_ctrl->CanSendChat()) {
chat_ctrl->SendChatTo("AI detected action item: ...");
}
}---
Scenario 4: Monitoring & Quality Bot
Goal: Monitor meeting quality metrics and alert on issues.
Core Pattern
// Get service quality info
auto* meeting_info = meeting_service->GetMeetingInfo();
// Polling loop (GLib timeout)
gboolean check_quality(gpointer data) {
// Audio stats
auto* audio_stats = meeting_info->GetAudioStatistics();
if (audio_stats) {
int jitter = audio_stats->jitter;
int packet_loss = audio_stats->packet_loss_percent;
if (packet_loss > 5) {
alert("High packet loss: " + std::to_string(packet_loss) + "%");
}
}
// Video stats
auto* video_stats = meeting_info->GetVideoStatistics();
if (video_stats) {
int fps = video_stats->fps;
int resolution_width = video_stats->width;
if (fps < 15) {
alert("Low FPS: " + std::to_string(fps));
}
}
return TRUE; // Continue polling
}
// Setup polling
g_timeout_add_seconds(10, check_quality, nullptr);---
Version Migration Guide
When SDK Updates
1. Read release notes for breaking changes 2. Check header files for actual method signatures 3. Test compilation with -Werror=deprecated 4. Update authentication if new token types added 5. Verify raw data flow still works
Common Breaking Changes
| Change Type | Example | Mitigation |
|---|---|---|
| Struct field rename | withoutloginuserJoin → withoutLoginUserJoin | Use #ifdef or update |
| Method signature change | subscribe(user_id) → subscribe(user_id, type) | Check return codes |
| Enum value rename | SDKERR_NORECORDINGINPROCESS → SDKERR_NO_RECORDING_IN_PROCESS | Map old → new |
| New required field | app_privilege_token becomes mandatory | Add to config |
Testing Strategy
// Version detection at compile time
#if ZOOM_SDK_VERSION >= 51500 // v5.15.0
#define USE_OBF_TOKEN
#endif
// Runtime capability testing
bool test_raw_recording() {
auto* ctrl = meeting_service->GetMeetingRecordingController();
return ctrl && ctrl->CanStartRawRecording() == SDKERR_SUCCESS;
}---
Docker Deployment Patterns
Multi-Stage Build (Version-Flexible)
FROM ubuntu:22.04 AS builder
# Install dependencies (stable)
RUN apt-get update && apt-get install -y \
build-essential cmake \
libx11-xcb1 libxcb-xfixes0 libxcb-shape0 \
libglib2.0-dev libcurl4-openssl-dev
# Copy SDK (any version)
COPY zoom-meeting-sdk-linux_*.tar.gz /tmp/
RUN cd /tmp && tar xzf zoom-meeting-sdk-linux_*.tar.gz
# Build app
COPY . /app
WORKDIR /app
RUN cmake -B build && cd build && make
# Runtime stage
FROM ubuntu:22.04
COPY --from=builder /app/build/meetingBot /usr/local/bin/
COPY --from=builder /tmp/lib*.so /usr/local/lib/
# PulseAudio setup (required for raw audio)
RUN apt-get update && apt-get install -y pulseaudio && \
mkdir -p ~/.config && \
echo "[General]\nsystem.audio.type=default" > ~/.config/zoomus.conf
CMD ["meetingBot"]---
Summary: Resilient Bot Checklist
✅ Always check capabilities before calling methods ✅ Use heap memory mode for raw data ✅ Handle authentication fallbacks (OBF → ZAK → password) ✅ Detect SDK version at compile/runtime ✅ Test with actual SDK headers, not documentation examples ✅ Setup PulseAudio for Docker/headless environments ✅ Parse error codes to adapt behavior ✅ Keep authentication tokens fresh (regenerate before expiry) ✅ Log everything for debugging version-specific issues
Zoom Meeting SDK (Linux)
Embed Zoom meeting capabilities into Linux applications for headless meeting bots and server-side integrations.
Prerequisites
- Zoom app with Meeting SDK credentials (Client ID & Secret)
- Docker (recommended) or Linux environment (Ubuntu 22+, CentOS 8/9)
- C++ development toolchain (cmake, gcc, g++)
- GLib 2.0, libcurl, pthread
Need help with authentication? See the [zoom-oauth](../../oauth/SKILL.md) skill for JWT token generation.
Overview
The Linux SDK is a C++ native SDK designed for:
- Headless meeting bots - Join meetings without UI
- Raw media access - Capture/send audio/video streams
- Server-side automation - Scalable meeting integrations
Quick Start
1. Download Linux SDK
Download from Zoom Marketplace:
- Extract
zoom-meeting-sdk-linux_x86_64-{version}.tar
2. Setup Project Structure
your-project/
demo/
include/h/ # SDK headers
lib/zoom_meeting_sdk/
libmeetingsdk.so
libmeetingsdk.so.1 # symlink
qt_libs/
json/translations.json
meeting_sdk_demo.cpp
CMakeLists.txt
config.txtCopy SDK files:
cp -r h/* demo/include/h/
cp lib*.so demo/lib/zoom_meeting_sdk/
cp -r qt_libs demo/lib/zoom_meeting_sdk/
cp translation.json demo/lib/zoom_meeting_sdk/json/
# Create required symlink
cd demo/lib/zoom_meeting_sdk/
ln -s libmeetingsdk.so libmeetingsdk.so.13. Configure Credentials
Create config.txt:
meeting_number: "1234567890"
token: "YOUR_JWT_TOKEN"
meeting_password: "password123"
recording_token: ""
GetVideoRawData: "true"
GetAudioRawData: "true"
SendVideoRawData: "false"
SendAudioRawData: "false"4. Build & Run
cd demo
cmake -B build
cd build
make
cd ../bin
./meetingSDKDemoCore Workflow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ InitSDK │───►│ AuthSDK │───►│ JoinMeeting │───►│ Raw Data │
│ │ │ (JWT) │ │ │ │ Subscribe │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
OnAuthComplete onInMeeting
callback callbackCode Examples
1. Initialize SDK
#include "zoom_sdk.h"
USING_ZOOM_SDK_NAMESPACE
void InitMeetingSDK() {
SDKError err(SDKERR_SUCCESS);
InitParam initParam;
initParam.strWebDomain = "https://zoom.us";
initParam.strSupportUrl = "https://zoom.us";
initParam.emLanguageID = LANGUAGE_English;
initParam.enableLogByDefault = true;
initParam.enableGenerateDump = true;
err = InitSDK(initParam);
if (err != SDKERR_SUCCESS) {
std::cerr << "Init meetingSdk:error" << std::endl;
}
}2. Authenticate with JWT
#include "auth_service_interface.h"
IAuthService* m_pAuthService;
void OnAuthenticationComplete() {
JoinMeeting(); // Called on successful auth
}
void AuthMeetingSDK() {
CreateAuthService(&m_pAuthService);
m_pAuthService->SetEvent(
new AuthServiceEventListener(&OnAuthenticationComplete)
);
AuthContext param;
param.jwt_token = token.c_str(); // Your JWT token
m_pAuthService->SDKAuth(param);
}3. Join Meeting
#include "meeting_service_interface.h"
IMeetingService* m_pMeetingService;
ISettingService* m_pSettingService;
void JoinMeeting() {
CreateMeetingService(&m_pMeetingService);
CreateSettingService(&m_pSettingService);
// Set event listeners
m_pMeetingService->SetEvent(
new MeetingServiceEventListener(&onMeetingJoined, &onMeetingEnds, &onInMeeting)
);
// Prepare join parameters
JoinParam joinParam;
joinParam.userType = SDK_UT_WITHOUT_LOGIN;
JoinParam4WithoutLogin& params = joinParam.param.withoutloginuserJoin;
params.meetingNumber = std::stoull(meeting_number);
params.userName = "BotUser";
params.psw = meeting_password.c_str();
params.isVideoOff = false;
params.isAudioOff = false;
m_pMeetingService->Join(joinParam);
}4. Subscribe to Raw Video
#include "rawdata/rawdata_renderer_interface.h"
#include "rawdata/zoom_rawdata_api.h"
class ZoomSDKRenderer : public IZoomSDKRendererDelegate {
public:
void onRawDataFrameReceived(YUVRawDataI420* data) override {
// YUV420 (I420) format - contiguous planar data (no strides)
int width = data->GetStreamWidth();
int height = data->GetStreamHeight();
// Y plane: width * height bytes
outputFile.write(data->GetYBuffer(), width * height);
// U plane: (width/2) * (height/2) bytes
outputFile.write(data->GetUBuffer(), (width / 2) * (height / 2));
// V plane: (width/2) * (height/2) bytes
outputFile.write(data->GetVBuffer(), (width / 2) * (height / 2));
}
void onRawDataStatusChanged(RawDataStatus status) override {}
void onRendererBeDestroyed() override {}
};
// Subscribe after joining
IZoomSDKRenderer* videoHelper;
ZoomSDKRenderer* videoSource = new ZoomSDKRenderer();
createRenderer(&videoHelper, videoSource);
videoHelper->setRawDataResolution(ZoomSDKResolution_720P);
videoHelper->subscribe(userID, RAW_DATA_TYPE_VIDEO);5. Subscribe to Raw Audio
#include "rawdata/rawdata_audio_helper_interface.h"
class ZoomSDKAudioRawData : public IZoomSDKAudioRawDataDelegate {
public:
void onMixedAudioRawDataReceived(AudioRawData* data) override {
// Process PCM audio (mixed from all participants)
pcmFile.write((char*)data->GetBuffer(), data->GetBufferLen());
}
void onOneWayAudioRawDataReceived(AudioRawData* data, uint32_t node_id) override {
// Process audio from specific participant
}
};
// Subscribe after joining
IZoomSDKAudioRawDataHelper* audioHelper = GetAudioRawdataHelper();
audioHelper->subscribe(new ZoomSDKAudioRawData());6. GLib Main Loop
#include <glib.h>
GMainLoop* loop;
gboolean timeout_callback(gpointer data) {
return TRUE; // Keep running
}
int main(int argc, char* argv[]) {
InitMeetingSDK();
AuthMeetingSDK();
loop = g_main_loop_new(NULL, FALSE);
g_timeout_add(1000, timeout_callback, loop);
g_main_loop_run(loop);
return 0;
}Available Examples
| Example | Description |
|---|---|
| SkeletonExample | Minimal join meeting - start here |
| GetRawVideoAndAudioExample | Subscribe to raw audio/video streams |
| GetRawVideoAndAudioAPIExample | API-based raw data access |
| SendRawVideoAndAudioExample | Send custom video/audio as virtual camera/mic |
| ChatExample | In-meeting chat functionality |
| BreakoutExample | Breakout room management |
| AllInOneExample | Complete demo with all features |
| SendRawVideoAndAudioWithRTMSExample | Raw data with RTMS integration |
Detailed References
High-Level Guides
- [concepts/high-level-scenarios.md](concepts/high-level-scenarios.md) - Production bot architectures (transcription, recording, AI assistant)
- [meeting-sdk-bot.md](meeting-sdk-bot.md) - Resilient bot pattern with retry logic
Platform Guide
- [references/linux-reference.md](references/linux-reference.md) - Dependencies, Docker setup, troubleshooting
Authentication
- [../references/authorization.md](../references/authorization.md) - SDK JWT generation
- [../references/bot-authentication.md](../references/bot-authentication.md) - Bot token types (ZAK, OBF, JWT)
Features
- [../references/breakout-rooms.md](../references/breakout-rooms.md) - Programmatic breakout room management
- [../references/ai-companion.md](../references/ai-companion.md) - AI Companion controls
Sample Repositories
| Repository | Description |
|---|---|
| meetingsdk-headless-linux-sample | Official headless bot with Docker |
| meetingsdk-linux-raw-recording-sample | Raw audio/video access |
Playing Raw Video/Audio Files
Raw YUV/PCM files have no headers - you must specify format explicitly.
Play Raw YUV Video
ffplay -video_size 640x360 -pixel_format yuv420p -f rawvideo video.yuvConvert YUV to MP4
ffmpeg -video_size 640x360 -pixel_format yuv420p -f rawvideo -i video.yuv -c:v libx264 output.mp4Play Raw PCM Audio
ffplay -f s16le -ar 32000 -ac 1 audio.pcmConvert PCM to WAV
ffmpeg -f s16le -ar 32000 -ac 1 -i audio.pcm output.wavCombine Video + Audio
ffmpeg -video_size 640x360 -pixel_format yuv420p -f rawvideo -i video.yuv \
-f s16le -ar 32000 -ac 1 -i audio.pcm \
-c:v libx264 -c:a aac -shortest output.mp4Key flags:
| Flag | Description |
|---|---|
-video_size WxH | Frame dimensions (check output filename) |
-pixel_format yuv420p | I420/YUV420 planar format |
-f rawvideo | Raw video input (no container) |
-f s16le | Signed 16-bit little-endian PCM |
-ar 32000 | Sample rate (Zoom uses 32kHz) |
-ac 1 | Mono (use -ac 2 for stereo) |
Compilation Tips
Note: These are general patterns - specific methods/types may vary by SDK version.
Event Listener Interfaces
SDK event listener interfaces (IMeetingServiceEvent, IMeetingParticipantsCtrlEvent, etc.) have many pure virtual methods. You must implement ALL of them, even with empty bodies, or you'll get "invalid new-expression of abstract class type" errors. Always check the SDK headers for the complete list.
Include Order Issues
Some SDK headers don't include their own dependencies. If you encounter undefined type errors (like AudioType or time_t), try:
- Adding standard library includes (
<ctime>,<cstdint>) before SDK headers - Including related SDK headers in dependency order
- Checking which header defines the missing type
Method Signature Mismatches
Reference samples may have outdated method names. Always verify against the actual SDK header files - they are the authoritative source.
Authentication Requirements (2026 Update)
Important: Beginning March 2, 2026, apps joining meetings outside their account must be authorized.
Use one of:
- App Privilege Token (OBF) - Recommended for bots (
app_privilege_tokenin JoinParam) - ZAK Token - Zoom Access Key (
userZAKin JoinParam) - On Behalf Token - For specific use cases (
onBehalfTokenin JoinParam)
Resources
- Official docs: https://developers.zoom.us/docs/meeting-sdk/linux/
- API Reference: https://marketplacefront.zoom.us/sdk/meeting/linux/index.html
- Developer forum: https://devforum.zoom.us/
- SDK download: https://marketplace.zoom.us/
Meeting SDK Bot (Linux)
Build resilient meeting bots that join Zoom meetings as visible participants using the Meeting SDK.
Overview
Meeting SDK bots join as real participants (visible in participant list) and can access raw audio/video data for recording, transcription, or AI processing.
Use this approach when:
- You need to be visible in the participant list
- You want to interact with meeting features (chat, reactions, etc.)
- You need local recording control
- You're processing your own meetings or have user consent
Alternative: See rtms/examples/rtms-bot.md for invisible, read-only access via RTMS.
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ MEETING SDK BOT FLOW │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 1. Pre-Join: REST API │
│ └── Get meeting schedule (number, password, start time) │
│ └── Get OBF token for user (bot joins "on behalf of" user) │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. Join with Retry (OBF requires owner present) │
│ └── Retry with configurable interval until owner joins │
│ └── Circuit breaker: Stop after N attempts │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 3. Start Raw Recording (Meeting SDK singleton) │
│ └── IMeetingRecordingController::StartRawRecording() │
│ └── Subscribe to raw audio/video │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. Process Media Streams │
│ └── Audio: PCM data via IZoomSDKAudioRawDataDelegate │
│ └── Video: YUV420 frames via IZoomSDKVideoRawDataDelegate │
└────────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 5. Mid-Meeting: Connection Monitoring │
│ └── Detect disconnections → Exponential backoff retry │
│ └── Stop after N reconnection attempts │
└─────────────────────────────────────────────────────────────────────┘Skills Required
| Skill | Purpose |
|---|---|
| zoom-rest-api | Get meeting schedule, retrieve OBF token |
| zoom-meeting-sdk (Linux) | Join meeting, control recording, access raw media |
Choose the Recording Mode
| Goal | Primary path | Skills |
|---|---|---|
| Bot writes its own audio/video files | StartRawRecording() + raw audio/video delegates | zoom-meeting-sdk (Linux) |
| Zoom-hosted MP4/M4A/transcript assets after meeting end | Meeting/account cloud recording settings + recording.completed webhook + recordings download API | zoom-rest-api + zoom-webhooks |
Use raw recording when the bot must process or persist media itself. Use the cloud-recording path when the requirement is post-meeting retrieval of Zoom-managed recording assets.
Automatic Join + Recording Flow
zoom-rest-api
-> get meeting metadata
-> get OBF or ZAK token
Meeting SDK Linux bot
-> join with retry
-> CanStartRawRecording()
-> StartRawRecording()
-> subscribe raw audio/video
-> write PCM/YUV or forward to AI pipeline
Optional post-meeting cloud path
-> zoom-webhooks recording.completed
-> zoom-rest-api recordings downloadPrerequisites
- Zoom Meeting SDK v5.15+ (Linux)
- Meeting SDK JWT credentials (SDK Key + Secret)
- Server-to-Server OAuth app or OAuth app for REST API access
- REST API scopes:
meeting:read,user:read, optionallymeeting:writeif triggering meetings - Raw Data entitlement enabled (Admin → Account Settings → Meeting → "Allow access to raw data")
Configuration
Retry Parameters (Customizable)
// config.h or environment variables
struct BotConfig {
// Join retry (waiting for owner to be present)
int join_retry_attempts = 5; // Max join attempts (default: 5)
int join_retry_interval_ms = 60000; // Constant interval: 60s (default: 1min)
// Mid-meeting reconnection (network failures)
int reconnect_max_attempts = 3; // Max reconnection attempts (default: 3)
int reconnect_base_delay_ms = 2000; // Initial delay: 2s (default: 2s)
// Exponential backoff: 2s, 4s, 8s...
// Meeting schedule polling (if webhook unavailable)
int schedule_poll_interval_ms = 30000; // Poll every 30s (default: 30s)
// Timeout settings
int auth_timeout_ms = 10000; // SDK auth timeout (default: 10s)
int join_timeout_ms = 30000; // Single join attempt timeout (default: 30s)
};
// Load from environment variables (recommended for production)
BotConfig loadConfig() {
BotConfig cfg;
// Override defaults from env vars if present
const char* env;
if ((env = getenv("BOT_JOIN_RETRY_ATTEMPTS"))) {
cfg.join_retry_attempts = atoi(env);
}
if ((env = getenv("BOT_JOIN_RETRY_INTERVAL_MS"))) {
cfg.join_retry_interval_ms = atoi(env);
}
if ((env = getenv("BOT_RECONNECT_MAX_ATTEMPTS"))) {
cfg.reconnect_max_attempts = atoi(env);
}
if ((env = getenv("BOT_RECONNECT_BASE_DELAY_MS"))) {
cfg.reconnect_base_delay_ms = atoi(env);
}
return cfg;
}Customization Guide
| Parameter | Default | When to Increase | When to Decrease |
|---|---|---|---|
join_retry_attempts | 5 | High-priority meetings, owner often late | Testing, short-lived meetings |
join_retry_interval_ms | 60000 (1min) | Meetings with long pre-join buffer | Need faster failure detection |
reconnect_max_attempts | 3 | Unstable networks, critical meetings | Batch processing, cost-sensitive |
reconnect_base_delay_ms | 2000 (2s) | Network latency high (international) | Local network, low latency |
Recommended Ranges:
- Join retry attempts: 3-10
- Join retry interval: 30s-5min
- Reconnect attempts: 2-5
- Reconnect base delay: 1s-5s
Examples:
# High-priority production bot (aggressive retries)
export BOT_JOIN_RETRY_ATTEMPTS=10
export BOT_JOIN_RETRY_INTERVAL_MS=30000 # 30s
export BOT_RECONNECT_MAX_ATTEMPTS=5
export BOT_RECONNECT_BASE_DELAY_MS=1000 # 1s
# Cost-sensitive batch processing (conservative)
export BOT_JOIN_RETRY_ATTEMPTS=3
export BOT_JOIN_RETRY_INTERVAL_MS=120000 # 2min
export BOT_RECONNECT_MAX_ATTEMPTS=2
export BOT_RECONNECT_BASE_DELAY_MS=5000 # 5s
# Development/testing (fail fast)
export BOT_JOIN_RETRY_ATTEMPTS=2
export BOT_JOIN_RETRY_INTERVAL_MS=10000 # 10s
export BOT_RECONNECT_MAX_ATTEMPTS=1
export BOT_RECONNECT_BASE_DELAY_MS=1000 # 1sStep 1: Get Meeting Info + OBF Token (REST API)
Get Meeting Schedule
# Get meeting details
curl "https://api.zoom.us/v2/meetings/{meetingId}" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{
"id": 1234567890,
"topic": "Team Standup",
"start_time": "2026-02-09T15:00:00Z",
"password": "abc123",
"pmi": false
}Store: meeting number, password, start time.
Get OBF Token
The bot joins "on behalf of" a Zoom user. Get the user's OBF token:
# Get OBF token for user
curl -X POST "https://api.zoom.us/v2/users/{userId}/token?type=obf&ttl=7200" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{
"token": "eyJhbGc...",
"expire_in": 7200
}CRITICAL: The bot cannot join until the owner (the user whose OBF token you're using) is present in the meeting. This is why retry logic is essential.
Error if REST API fails: ABORT. No meeting info = cannot proceed.
Step 2: Join Meeting with Retry Logic
Join Implementation
#include <chrono>
#include <thread>
class MeetingBot {
private:
BotConfig config;
IMeetingService* meetingService;
bool joinSuccessful = false;
bool ownerNotPresentError = false;
public:
// Attempt to join with retry logic
bool joinMeetingWithRetry(
uint64_t meetingNumber,
const string& password,
const string& obfToken,
const string& botDisplayName
) {
for (int attempt = 1; attempt <= config.join_retry_attempts; attempt++) {
cout << "[JOIN] Attempt " << attempt << "/"
<< config.join_retry_attempts << endl;
// Attempt join
JoinParam joinParam;
joinParam.userType = SDK_UT_WITHOUT_LOGIN;
JoinParam4WithoutLogin& param = joinParam.param.withoutloginuserJoin;
param.meetingNumber = meetingNumber;
param.userName = botDisplayName.c_str();
param.psw = password.c_str();
param.isVideoOff = true; // Bots typically don't send video
param.isAudioOff = false; // Need audio for transcription
param.app_privilege_token = obfToken.c_str(); // OBF token
SDKError err = meetingService->Join(joinParam);
if (err != SDKERR_SUCCESS) {
cerr << "[JOIN] Join() call failed: " << err << endl;
// Check if it's a retriable error
if (shouldRetryJoin(err)) {
if (attempt < config.join_retry_attempts) {
cout << "[JOIN] Retrying in "
<< (config.join_retry_interval_ms / 1000)
<< "s..." << endl;
this_thread::sleep_for(
chrono::milliseconds(config.join_retry_interval_ms)
);
continue;
}
}
// Non-retriable error or out of attempts
cerr << "[JOIN] Giving up after " << attempt << " attempts" << endl;
return false;
}
// Join() call succeeded, wait for callback
if (waitForJoinCallback()) {
cout << "[JOIN] Successfully joined meeting!" << endl;
return true;
} else {
cerr << "[JOIN] Join callback indicated failure" << endl;
// If owner not present, retry
if (ownerNotPresentError && attempt < config.join_retry_attempts) {
cout << "[JOIN] Owner not present. Retrying in "
<< (config.join_retry_interval_ms / 1000) << "s..." << endl;
this_thread::sleep_for(
chrono::milliseconds(config.join_retry_interval_ms)
);
continue;
}
}
}
cerr << "[JOIN] Failed to join after "
<< config.join_retry_attempts << " attempts" << endl;
return false;
}
private:
bool shouldRetryJoin(SDKError err) {
switch (err) {
case SDKERR_WRONG_USAGE:
case SDKERR_INVALID_PARAMETER:
case SDKERR_MODULE_LOAD_FAILED:
return false; // Configuration errors, don't retry
case SDKERR_NETWORK_ERROR:
case SDKERR_SERVICE_FAILED:
return true; // Network/server issues, retry
default:
return true; // Unknown error, retry to be safe
}
}
bool waitForJoinCallback() {
// Wait for onMeetingStatusChanged callback
// Implementation depends on your callback handling
// Typical: use condition variable with timeout
std::unique_lock<std::mutex> lock(callbackMutex);
bool success = callbackCV.wait_for(
lock,
std::chrono::milliseconds(config.join_timeout_ms),
[this]{ return joinSuccessful || ownerNotPresentError; }
);
return success && joinSuccessful;
}
public:
// Callback from SDK
void onMeetingStatusChanged(MeetingStatus status, int iResult) {
std::lock_guard<std::mutex> lock(callbackMutex);
if (status == MEETING_STATUS_INMEETING) {
joinSuccessful = true;
callbackCV.notify_one();
} else if (status == MEETING_STATUS_FAILED) {
// Check error code for "owner not present"
if (iResult == MEETING_FAIL_OBF_OWNER_NOT_IN_MEETING) {
ownerNotPresentError = true;
}
joinSuccessful = false;
callbackCV.notify_one();
}
}
private:
std::mutex callbackMutex;
std::condition_variable callbackCV;
};Common Join Errors
| Error Code | Meaning | Action |
|---|---|---|
MEETING_FAIL_OBF_OWNER_NOT_IN_MEETING | OBF token owner not in meeting yet | RETRY (owner might join soon) |
MEETING_FAIL_MEETING_NOT_EXIST | Meeting not started | RETRY if before end time |
MEETING_FAIL_INCORRECT_MEETING_NUMBER | Wrong meeting ID | ABORT (config error) |
MEETING_FAIL_MEETING_NOT_START | Meeting hasn't started | RETRY until start time |
MEETING_FAIL_INVALID_TOKEN | OBF token invalid/expired | ABORT (need new token) |
Step 3: Start Raw Recording
Once joined, request permission to access raw audio/video:
void onMeetingStatusChanged(MeetingStatus status, int iResult) {
if (status == MEETING_STATUS_INMEETING) {
cout << "[BOT] Joined successfully, starting raw recording..." << endl;
// Get recording controller
IMeetingRecordingController* recordCtrl =
meetingService->GetMeetingRecordingController();
if (!recordCtrl) {
cerr << "[BOT] Failed to get recording controller" << endl;
return;
}
// Check permission
SDKError canRecord = recordCtrl->CanStartRawRecording();
if (canRecord != SDKERR_SUCCESS) {
cerr << "[BOT] Cannot start raw recording: " << canRecord << endl;
cerr << "[BOT] Check: Raw Data entitlement enabled in Admin settings?" << endl;
return;
}
// Start raw recording (enables raw data flow)
SDKError err = recordCtrl->StartRawRecording();
if (err != SDKERR_SUCCESS) {
cerr << "[BOT] StartRawRecording failed: " << err << endl;
return;
}
cout << "[BOT] Raw recording started, subscribing to media..." << endl;
// Subscribe to audio/video
subscribeToRawMedia();
}
}IMPORTANT: StartRawRecording() does NOT create a file. It enables access to raw audio/video data streams.
Manage Recording Lifecycle Explicitly
The bot should treat raw recording as a capability switch plus media subscriptions:
class RecordingSession {
public:
void start(IMeetingService* meetingService) {
auto* recordCtrl = meetingService->GetMeetingRecordingController();
if (!recordCtrl) {
throw std::runtime_error("recording_controller_unavailable");
}
SDKError canRecord = recordCtrl->CanStartRawRecording();
if (canRecord != SDKERR_SUCCESS) {
throw std::runtime_error("raw_recording_not_permitted");
}
SDKError err = recordCtrl->StartRawRecording();
if (err != SDKERR_SUCCESS) {
throw std::runtime_error("start_raw_recording_failed");
}
audioHelper = GetAudioRawdataHelper();
audioHelper->subscribe(&audioDelegate, true);
createRenderer(&videoRenderer, &videoDelegate);
videoRenderer->setRawDataResolution(ZoomSDKResolution_720P);
videoRenderer->subscribe(activeUserId, RAW_DATA_TYPE_VIDEO);
}
void stop(IMeetingService* meetingService) {
if (audioHelper) {
audioHelper->unSubscribe();
}
if (videoRenderer) {
videoRenderer->unSubscribe();
}
auto* recordCtrl = meetingService->GetMeetingRecordingController();
if (recordCtrl) {
recordCtrl->StopRawRecording();
}
}
private:
IZoomSDKAudioRawDataHelper* audioHelper = nullptr;
IZoomSDKRenderer* videoRenderer = nullptr;
MyAudioDelegate audioDelegate;
MyVideoDelegate videoDelegate;
uint32_t activeUserId = 0;
};Persisting a recording is your job after raw data arrives. Typical outputs are:
- PCM audio -> WAV/FLAC encoder or streaming transcription pipeline
- YUV420 video -> FFmpeg transcode to MP4 or frame-by-frame CV pipeline
- Mixed bot pipeline -> raw capture first, then post-process after leave
Step 4: Subscribe to Raw Audio/Video
void subscribeToRawMedia() {
// Subscribe to raw audio
IZoomSDKAudioRawDataDelegate* audioDelegate = new MyAudioDelegate();
SDKError audioErr = meetingService->GetMeetingAudioController()
->GetMeetingAudioHelper()
->subscribe(audioDelegate, true); // true = mixed audio
if (audioErr != SDKERR_SUCCESS) {
cerr << "[AUDIO] Subscribe failed: " << audioErr << endl;
} else {
cout << "[AUDIO] Subscribed to mixed audio" << endl;
}
// Subscribe to raw video
IZoomSDKVideoRawDataDelegate* videoDelegate = new MyVideoDelegate();
SDKError videoErr = meetingService->GetMeetingVideoController()
->GetMeetingVideoHelper()
->subscribe(videoDelegate);
if (videoErr != SDKERR_SUCCESS) {
cerr << "[VIDEO] Subscribe failed: " << videoErr << endl;
} else {
cout << "[VIDEO] Subscribed to video streams" << endl;
}
}Cloud Recording Alternative
If the requirement is Zoom-managed cloud recording instead of raw media capture, use Meeting SDK only for the joining bot and use API/webhook skills for the recording workflow:
curl -X POST "https://api.zoom.us/v2/users/{userId}/meetings" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"topic": "Bot Recorded Meeting",
"type": 2,
"start_time": "2026-03-06T18:00:00Z",
"settings": {
"auto_recording": "cloud"
}
}'Then subscribe to recording.completed and download assets through the recordings APIs:
zoom-webhooks-> receiverecording.completedzoom-rest-api->GET /meetings/{meetingId}/recordingsorGET /users/{userId}/recordings
Use this path when the desired output is Zoom-hosted MP4/M4A/transcript files rather than bot-owned raw PCM/YUV.
Step 5: Mid-Meeting Reconnection
Connection Monitoring
class MeetingBot {
private:
BotConfig config;
int reconnectionAttempt = 0;
public:
void onMeetingStatusChanged(MeetingStatus status, int iResult) {
switch (status) {
case MEETING_STATUS_RECONNECTING:
cout << "[BOT] Connection lost, SDK is reconnecting..." << endl;
break;
case MEETING_STATUS_FAILED:
case MEETING_STATUS_DISCONNECTING:
handleDisconnection(iResult);
break;
case MEETING_STATUS_INMEETING:
// Reconnection successful
if (reconnectionAttempt > 0) {
cout << "[BOT] Reconnected successfully!" << endl;
reconnectionAttempt = 0; // Reset counter
}
break;
}
}
private:
void handleDisconnection(int errorCode) {
reconnectionAttempt++;
cout << "[RECONNECT] Disconnected (error: " << errorCode
<< "), attempt " << reconnectionAttempt << "/"
<< config.reconnect_max_attempts << endl;
if (reconnectionAttempt >= config.reconnect_max_attempts) {
cerr << "[RECONNECT] Giving up after "
<< reconnectionAttempt << " attempts" << endl;
cleanup();
notifyFailure("Max reconnection attempts exceeded");
return;
}
// Exponential backoff: 2s, 4s, 8s...
int delay_ms = config.reconnect_base_delay_ms
* (1 << (reconnectionAttempt - 1));
cout << "[RECONNECT] Retrying in " << (delay_ms / 1000) << "s..." << endl;
// Schedule reconnection
std::thread([this, delay_ms]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
attemptRejoin();
}).detach();
}
void attemptRejoin() {
// Re-use same meeting number, password, OBF token
// If OBF token expired, fetch new one from REST API first
if (isOBFTokenExpired()) {
cout << "[RECONNECT] OBF token expired, fetching new one..." << endl;
// TODO: Call REST API to get fresh OBF token
}
// Call join again
bool success = joinMeetingWithRetry(
cachedMeetingNumber,
cachedPassword,
cachedOBFToken,
cachedBotName
);
if (!success) {
cerr << "[RECONNECT] Rejoin failed" << endl;
cleanup();
notifyFailure("Reconnection failed");
}
}
};Customizing Reconnection Behavior
// Example: Linear backoff instead of exponential
int delay_ms = config.reconnect_base_delay_ms * reconnectionAttempt;
// Example: Capped exponential backoff (max 30s)
int delay_ms = std::min(
config.reconnect_base_delay_ms * (1 << (reconnectionAttempt - 1)),
30000 // Cap at 30s
);
// Example: Jittered backoff (avoid thundering herd)
int base_delay = config.reconnect_base_delay_ms * (1 << (reconnectionAttempt - 1));
int jitter = rand() % 1000; // Random 0-1000ms
int delay_ms = base_delay + jitter;Recording Fallback: Local vs Cloud
If raw recording fails, fall back to local or cloud recording:
void startRecordingWithFallback() {
IMeetingRecordingController* ctrl =
meetingService->GetMeetingRecordingController();
// Try raw recording first
if (ctrl->CanStartRawRecording() == SDKERR_SUCCESS) {
SDKError err = ctrl->StartRawRecording();
if (err == SDKERR_SUCCESS) {
cout << "[RECORDING] Using raw recording" << endl;
return;
}
}
// Fallback: Local recording
if (ctrl->CanStartRecording(true) == SDKERR_SUCCESS) {
SDKError err = ctrl->StartRecording(
chrono::system_clock::now(),
"/tmp/bot_recording.mp4"
);
if (err == SDKERR_SUCCESS) {
cout << "[RECORDING] Using local recording" << endl;
return;
}
}
// Fallback: Cloud recording
if (ctrl->CanStartCloudRecording() == SDKERR_SUCCESS) {
SDKError err = ctrl->StartCloudRecording();
if (err == SDKERR_SUCCESS) {
cout << "[RECORDING] Using cloud recording" << endl;
return;
}
}
cerr << "[RECORDING] All recording methods failed" << endl;
}Complete Resilient Bot Example
int main() {
// 1. Load configuration
BotConfig config = loadConfig();
// 2. Initialize Meeting SDK
InitParam initParam;
initParam.strWebDomain = "https://zoom.us";
initParam.emLanguageID = LANGUAGE_English;
initParam.enableLogByDefault = true;
SDKError err = InitSDK(initParam);
if (err != SDKERR_SUCCESS) {
cerr << "InitSDK failed: " << err << endl;
return 1;
}
// 3. Authenticate SDK with JWT
AuthContext authCtx;
authCtx.jwt_token = generateJWT(SDK_KEY, SDK_SECRET);
IAuthService* authService = CreateAuthService();
authService->SDKAuth(authCtx);
// Wait for auth callback...
// 4. Fetch meeting info + OBF token from REST API
MeetingInfo meetingInfo = fetchMeetingInfoFromAPI(MEETING_ID);
string obfToken = fetchOBFTokenFromAPI(USER_ID);
if (meetingInfo.empty() || obfToken.empty()) {
cerr << "ABORT: Failed to get meeting info or OBF token" << endl;
return 1;
}
// 5. Join meeting with retry
MeetingBot bot(config);
bool joined = bot.joinMeetingWithRetry(
meetingInfo.number,
meetingInfo.password,
obfToken,
"Transcription Bot"
);
if (!joined) {
cerr << "ABORT: Failed to join meeting" << endl;
return 1;
}
// 6. SDK callbacks handle: StartRawRecording, subscribe, reconnection
// 7. Keep running until meeting ends
bot.runEventLoop();
// 8. Cleanup
bot.cleanup();
CleanUPSDK();
return 0;
}Comparison: Meeting SDK Bot vs RTMS Bot
| Aspect | Meeting SDK Bot | RTMS Bot |
|---|---|---|
| Visibility | Visible participant | Invisible (read-only service) |
| Authentication | JWT + OBF token | REST API trigger + webhook |
| Join Dependency | Owner must be present | No dependency on participants |
| Retry Logic | Required (owner presence) | Not applicable (webhook-based) |
| Media Access | Raw audio/video/share via SDK | Audio/video/text/share/chat via WebSocket |
| Recording Control | Full (local, cloud, raw) | None (read-only) |
| Interaction | Can send chat, reactions | Cannot interact |
| Resource Usage | Higher (full SDK) | Lower (WebSocket only) |
| Use Case | Interactive bots, recording, moderation | Passive transcription, analytics |
Choose Meeting SDK Bot when:
- You need to interact with the meeting (chat, reactions)
- You need local recording control
- You want to be visible in participant list
- You're processing your own meetings
Choose RTMS Bot when:
- You only need to observe/transcribe
- You want minimal resource usage
- You prefer invisible operation
- You're processing external meetings
Troubleshooting
Bot Never Joins (OBF Owner Not Present)
Symptom: All join attempts fail with "owner not in meeting"
Solution: 1. Verify owner (OBF token holder) has joined the meeting 2. Increase join_retry_attempts or join_retry_interval_ms 3. Check meeting start time - don't attempt join before meeting starts 4. Consider webhook: Listen for meeting.participant_joined event for owner
Raw Recording Permission Denied
Symptom: CanStartRawRecording() returns error
Solution: 1. Admin → Account Settings → Meeting → In Meeting (Advanced) 2. Enable "Allow access to raw data (audio, video, sharing) for Meeting SDK" 3. Requires "Raw Data" entitlement from Zoom support
Frequent Disconnections During Meeting
Symptom: Bot reconnects multiple times, then gives up
Solution: 1. Increase reconnect_max_attempts (e.g., 5 instead of 3) 2. Increase reconnect_base_delay_ms if network is slow 3. Check server network stability 4. Monitor CPU/memory usage (insufficient resources can cause disconnects)
OBF Token Expires During Meeting
Symptom: Reconnection fails with "invalid token"
Solution: 1. Fetch fresh OBF token in attemptRejoin() before rejoining 2. Monitor token expiration (expire_in from API response) 3. Request longer-lived tokens (max TTL: 7200s = 2 hours)
Resources
- Meeting SDK Linux: https://developers.zoom.us/docs/meeting-sdk/linux/
- Raw Data Guide: https://developers.zoom.us/docs/meeting-sdk/linux/add-features/raw-data/
- OBF FAQ: https://developers.zoom.us/docs/meeting-sdk/obf-faq/
- REST API - Get Meeting: https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/meeting
- REST API - Get OBF Token: https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/userToken
- RTMS Bot Alternative: ../../rtms/examples/rtms-bot.md
Meeting SDK - Linux Reference
Complete reference for Zoom Meeting SDK on Linux including dependencies, Docker setup, and troubleshooting.
System Requirements
- OS: Ubuntu 22+, CentOS 8/9, Oracle Linux 8
- Architecture: x86_64
- Build Tools: cmake 3.16+, gcc/g++ with C++11 support
Dependencies
Ubuntu 22/23
# Build essentials
apt-get update
apt-get install -y build-essential cmake
# X11/XCB libraries (required by SDK)
apt-get install -y --no-install-recommends --no-install-suggests \
libx11-xcb1 \
libxcb-xfixes0 \
libxcb-shape0 \
libxcb-shm0 \
libxcb-randr0 \
libxcb-image0 \
libxcb-keysyms1 \
libxcb-xtest0
# Optional but recommended
apt-get install -y --no-install-recommends --no-install-suggests \
libdbus-1-3 \
libglib2.0-0 \
libgbm1 \
libxfixes3 \
libgl1 \
libdrm2 \
libgssapi-krb5-2
# For curl-based JWT fetching
apt-get install -y \
libcurl4-openssl-dev \
openssl \
ca-certificates \
pkg-config
# Audio support (PulseAudio)
apt-get install -y \
libasound2 \
libasound2-plugins \
alsa \
alsa-utils \
alsa-oss \
pulseaudio \
pulseaudio-utils
# Optional: FFmpeg for video processing
apt-get install -y libavformat-dev libavfilter-dev libavdevice-dev ffmpeg
# If SDL2 errors occur
apt-get install -y libegl-mesa0 libsdl2-dev g++-multilibCentOS 8/9
# Build essentials
sudo yum install cmake gcc gcc-c++
# Enable required repos (CentOS 9)
sudo dnf config-manager --set-enabled crb
sudo dnf install epel-release epel-next-release
# XCB libraries
sudo yum install \
libxcb-devel \
xcb-util-devel \
xcb-util-image \
xcb-util-keysyms
# OpenGL/Mesa (for runtime)
sudo yum install \
mesa-libGL \
mesa-libGL-devel \
mesa-dri-drivers
# Curl support
sudo yum install -y openssl-devel libcurl-devel
# PulseAudio
sudo yum install -y pulseaudio pulseaudio-utils
# Optional: FFmpeg
sudo yum install -y libavformat-dev libavfilter-dev libavdevice-dev ffmpeg
# If SDL2 errors occur
sudo yum install -y SDL2-develCMakeLists.txt Template
cmake_minimum_required(VERSION 3.16)
project(meetingSDKDemo CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O0")
add_definitions(-std=c++11)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
find_package(PkgConfig REQUIRED)
find_package(ZLIB REQUIRED)
# GLib (required for main loop)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GIO REQUIRED gio-2.0)
# Include directories
include_directories(${CMAKE_SOURCE_DIR}/include)
include_directories(${CMAKE_SOURCE_DIR}/include/h)
include_directories(${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS})
# Common GLib paths
include_directories(/usr/include/glib-2.0/)
include_directories(/usr/include/glib-2.0/glib)
include_directories(/usr/lib/x86_64-linux-gnu/glib-2.0/include/)
include_directories(/usr/lib64/glib-2.0/include/)
# Link directories
link_directories(${GLIB_LIBRARY_DIRS} ${GIO_LIBRARY_DIRS})
link_directories(${CMAKE_SOURCE_DIR}/lib/zoom_meeting_sdk)
link_directories(${CMAKE_SOURCE_DIR}/lib/zoom_meeting_sdk/qt_libs)
link_directories(${CMAKE_SOURCE_DIR}/lib/zoom_meeting_sdk/qt_libs/Qt/lib)
add_definitions(${GLIB_CFLAGS_OTHER} ${GIO_CFLAGS_OTHER})
# Source files
add_executable(meetingSDKDemo
${CMAKE_SOURCE_DIR}/meeting_sdk_demo.cpp
${CMAKE_SOURCE_DIR}/AuthServiceEventListener.cpp
${CMAKE_SOURCE_DIR}/MeetingServiceEventListener.cpp
${CMAKE_SOURCE_DIR}/MeetingReminderEventListener.cpp
${CMAKE_SOURCE_DIR}/MeetingParticipantsCtrlEventListener.cpp
${CMAKE_SOURCE_DIR}/MeetingRecordingCtrlEventListener.cpp
# Raw data handlers (if needed)
${CMAKE_SOURCE_DIR}/ZoomSDKRenderer.cpp
${CMAKE_SOURCE_DIR}/ZoomSDKAudioRawData.cpp
${CMAKE_SOURCE_DIR}/ZoomSDKVideoSource.cpp
${CMAKE_SOURCE_DIR}/ZoomSDKVirtualAudioMicEvent.cpp
)
# Link libraries
target_link_libraries(meetingSDKDemo ${GLIB_LIBRARIES} ${GIO_LIBRARIES})
target_link_libraries(meetingSDKDemo gcc_s gcc)
target_link_libraries(meetingSDKDemo meetingsdk)
target_link_libraries(meetingSDKDemo glib-2.0)
target_link_libraries(meetingSDKDemo curl)
target_link_libraries(meetingSDKDemo pthread)
# Create symlink for SDK
execute_process(COMMAND ln -sf libmeetingsdk.so libmeetingsdk.so.1
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lib/zoom_meeting_sdk
)
# Copy config to bin
configure_file(${CMAKE_SOURCE_DIR}/config.txt ${CMAKE_SOURCE_DIR}/bin/config.txt COPYONLY)
# Copy SDK files to bin
file(COPY ${CMAKE_SOURCE_DIR}/lib/zoom_meeting_sdk/ DESTINATION ${CMAKE_SOURCE_DIR}/bin)Configuration File
config.txt Format
meeting_number: "1234567890"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
meeting_password: "abc123"
recording_token: ""
onBehalfOf_Token: ""
GetVideoRawData: "true"
GetAudioRawData: "true"
SendVideoRawData: "false"
SendAudioRawData: "false"config.json Format (Alternative)
{
"meeting_number": "1234567890",
"token": "YOUR_JWT_TOKEN",
"meeting_password": "abc123",
"recording_token": "",
"remote_url": "https://your-auth-endpoint.com",
"useJWTTokenFromWebService": "false",
"useRecordingTokenFromWebService": "false"
}Docker Setup
Dockerfile (Ubuntu)
FROM ubuntu:22.04
# Prevent interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential cmake \
libx11-xcb1 libxcb-xfixes0 libxcb-shape0 libxcb-shm0 \
libxcb-randr0 libxcb-image0 libxcb-keysyms1 libxcb-xtest0 \
libdbus-1-3 libglib2.0-0 libgbm1 libxfixes3 libgl1 libdrm2 \
libcurl4-openssl-dev openssl ca-certificates pkg-config \
pulseaudio pulseaudio-utils \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy project files
COPY . .
# Build
RUN cd demo && cmake -B build && cd build && make
# Setup audio config
RUN mkdir -p ~/.config && \
echo "[General]" > ~/.config/zoomus.conf && \
echo "system.audio.type=default" >> ~/.config/zoomus.conf
CMD ["./demo/bin/meetingSDKDemo"]PulseAudio Setup (Headless)
Create setup-pulseaudio.sh:
#!/bin/bash
# Start PulseAudio daemon
pulseaudio --start --exit-idle-time=-1
# Create virtual speaker
pactl load-module module-null-sink sink_name=virtual_speaker sink_properties=device.description="Virtual_Speaker"
# Create virtual microphone
pactl load-module module-null-sink sink_name=virtual_mic sink_properties=device.description="Virtual_Microphone"
# Create zoomus.conf
mkdir -p ~/.config
cat > ~/.config/zoomus.conf << EOF
[General]
system.audio.type=default
EOF
echo "PulseAudio configured for headless operation"Docker Compose
version: '3.8'
services:
meeting-bot:
build: .
environment:
- PULSE_SERVER=unix:/run/user/1000/pulse/native
volumes:
- ./config.txt:/app/demo/bin/config.txt
- /run/user/1000/pulse:/run/user/1000/pulse
network_mode: hostEvent Listeners
AuthServiceEventListener
// AuthServiceEventListener.h
#include "auth_service_interface.h"
class AuthServiceEventListener : public IAuthServiceEvent {
public:
AuthServiceEventListener(void (*onComplete)())
: onAuthComplete(onComplete) {}
void onAuthenticationReturn(AuthResult ret) override {
if (ret == AUTHRET_SUCCESS && onAuthComplete) {
onAuthComplete();
}
}
void onLoginReturnWithReason(LOGINSTATUS ret, IAccountInfo* info, LoginFailReason reason) override {}
void onLogout() override {}
void onZoomIdentityExpired() override {}
void onZoomAuthIdentityExpired() override {}
private:
void (*onAuthComplete)();
};MeetingServiceEventListener
// MeetingServiceEventListener.h
#include "meeting_service_interface.h"
class MeetingServiceEventListener : public IMeetingServiceEvent {
public:
MeetingServiceEventListener(
void (*onJoined)(),
void (*onEnded)(),
void (*onInMeeting)()
) : onMeetingJoined(onJoined),
onMeetingEnded(onEnded),
onInMeetingCallback(onInMeeting) {}
void onMeetingStatusChanged(MeetingStatus status, int iResult) override {
if (status == MEETING_STATUS_CONNECTING) {
if (onMeetingJoined) onMeetingJoined();
}
else if (status == MEETING_STATUS_INMEETING) {
if (onInMeetingCallback) onInMeetingCallback();
}
else if (status == MEETING_STATUS_ENDED) {
if (onMeetingEnded) onMeetingEnded();
}
}
void onMeetingStatisticsWarningNotification(StatisticsWarningType type) override {}
void onMeetingParameterNotification(const MeetingParameter* param) override {}
void onSuspendParticipantsActivities() override {}
void onAICompanionActiveChangeNotice(bool isActive) override {}
private:
void (*onMeetingJoined)();
void (*onMeetingEnded)();
void (*onInMeetingCallback)();
};MeetingParticipantsCtrlEventListener
// MeetingParticipantsCtrlEventListener.h
#include "meeting_service_components/meeting_participants_ctrl_interface.h"
class MeetingParticipantsCtrlEventListener : public IMeetingParticipantsCtrlEvent {
public:
MeetingParticipantsCtrlEventListener(
void (*onHost)(),
void (*onCoHost)()
) : onIsHost(onHost), onIsCoHost(onCoHost) {}
void onUserJoin(IList<unsigned int>* lstUserID, const zchar_t* strUserList) override {}
void onUserLeft(IList<unsigned int>* lstUserID, const zchar_t* strUserList) override {}
void onHostChangeNotification(unsigned int userId) override {
if (onIsHost) onIsHost();
}
void onCoHostChangeNotification(unsigned int userId, bool isCoHost) override {
if (isCoHost && onIsCoHost) onIsCoHost();
}
void onLowOrRaiseHandStatusChanged(bool bLow, unsigned int userid) override {}
private:
void (*onIsHost)();
void (*onIsCoHost)();
};Raw Data Requirements
Recording Permission
Raw data access requires one of: 1. Host status 2. Co-host status 3. Recording permission from host 4. Recording token (app_privilege_token)
// Check and start raw recording
void CheckAndStartRawRecording() {
IMeetingRecordingController* recordCtrl =
m_pMeetingService->GetMeetingRecordingController();
SDKError canStart = recordCtrl->CanStartRawRecording();
if (canStart == SDKERR_SUCCESS) {
recordCtrl->StartRawRecording();
// Now subscribe to raw data
} else {
std::cout << "Need host/cohost/recording permission" << std::endl;
}
}Video Resolutions
videoHelper->setRawDataResolution(ZoomSDKResolution_90P);
videoHelper->setRawDataResolution(ZoomSDKResolution_180P);
videoHelper->setRawDataResolution(ZoomSDKResolution_360P);
videoHelper->setRawDataResolution(ZoomSDKResolution_720P);
videoHelper->setRawDataResolution(ZoomSDKResolution_1080P);Audio Format
- Format: PCM (raw)
- Sample Rate: 32000 Hz
- Channels: Mono or Stereo
- Bit Depth: 16-bit
Compilation Tips
Note: Specific methods/types may vary by SDK version. Always check SDK headers.
Event Listener Interfaces
SDK event listener interfaces have many pure virtual methods. Implement ALL of them (even with empty bodies) or you'll get "invalid new-expression of abstract class type" errors. Check the SDK header for the complete interface definition.
Include Order Issues
Some SDK headers don't include their dependencies. If you get undefined type errors:
- Add standard includes (
<ctime>,<cstdint>) before SDK headers - Include related SDK headers in dependency order (e.g.,
meeting_audio_interface.hbeforemeeting_participants_ctrl_interface.h)
Method Signatures
Reference samples may have outdated method names. The SDK header files are the authoritative source - always verify method signatures there.
Troubleshooting
Segmentation Fault on AuthSDK
Cause: OpenSSL version incompatibility (needs 1.1.x)
Fix (Ubuntu):
cd /tmp
wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.20_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.20_amd64.deblibGL Error (Mesa)
libGL error: MESA-LOADER: failed to open swrastFix:
# Ubuntu
apt-get install mesa-libGL mesa-dri-drivers
# CentOS
yum install mesa-libGL mesa-libGL-devel mesa-dri-driversXCB Libraries Missing
libxcb-image.so.0 not found
libxcb-keysyms.so.1 not foundFix:
# Ubuntu
apt-get install libxcb-image0 libxcb-keysyms1
# CentOS
yum install xcb-util-image xcb-util-keysymsNo Audio in Docker
Fix: Create zoomus.conf before running:
mkdir -p ~/.config
echo -e "[General]\nsystem.audio.type=default" > ~/.config/zoomus.confCannot Start Raw Recording
Cause: No recording permission
Fix: Either: 1. Wait for host to grant recording permission 2. Use recording_token in config (get from Recording Token API) 3. Join as host using onBehalfOf_Token
Cleanup
void CleanSDK() {
if (videoHelper) videoHelper->unSubscribe();
if (audioHelper) audioHelper->unSubscribe();
if (m_pAuthService) {
DestroyAuthService(m_pAuthService);
m_pAuthService = NULL;
}
if (m_pSettingService) {
DestroySettingService(m_pSettingService);
m_pSettingService = NULL;
}
if (m_pMeetingService) {
DestroyMeetingService(m_pMeetingService);
m_pMeetingService = NULL;
}
CleanUPSDK();
}API Reference
InitParam Structure
struct tagInitParam {
const zchar_t* strWebDomain; // "https://zoom.us"
const zchar_t* strBrandingName; // Custom branding name
const zchar_t* strSupportUrl; // Support URL
SDK_LANGUAGE_ID emLanguageID; // LANGUAGE_English, etc.
bool enableGenerateDump; // Enable crash dump
bool enableLogByDefault; // Enable logging
unsigned int uiLogFileSize; // Log file size (MB, default: 5)
RawDataOptions rawdataOpts; // Raw data options
ConfigurableOptions obConfigOpts; // Config options
int wrapperType; // SDK wrapper type
};IAuthService Methods
| Method | Description | Returns |
|---|---|---|
SetEvent(IAuthServiceEvent*) | Set auth callback | SDKError |
SDKAuth(AuthContext&) | Authenticate with JWT | SDKError |
GetAuthResult() | Get auth status | AuthResult |
GetSDKIdentity() | Get SDK identity | const zchar_t* |
LogOut() | Logout | SDKError |
GetAccountInfo() | Get account info | IAccountInfo* |
GetLoginStatus() | Get login status | LOGINSTATUS |
IMeetingService Methods
| Method | Description | Returns |
|---|---|---|
SetEvent(IMeetingServiceEvent*) | Set meeting callback | SDKError |
Join(JoinParam&) | Join meeting | SDKError |
Start(StartParam&) | Start meeting | SDKError |
Leave(LeaveMeetingCmd) | Leave meeting | SDKError |
GetMeetingStatus() | Get status | MeetingStatus |
GetMeetingInfo() | Get meeting info | IMeetingInfo* |
GetMeetingVideoController() | Video control | IMeetingVideoController* |
GetMeetingAudioController() | Audio control | IMeetingAudioController* |
GetMeetingRecordingController() | Recording control | IMeetingRecordingController* |
GetMeetingParticipantsController() | Participants | IMeetingParticipantsController* |
GetMeetingChatController() | Chat control | IMeetingChatController* |
JoinParam4WithoutLogin Structure
struct JoinParam4WithoutLogin {
UINT64 meetingNumber; // Meeting number
const zchar_t* userName; // Display name
const zchar_t* psw; // Meeting password
const zchar_t* vanityID; // Personal link name
const zchar_t* customer_key; // Customer key
const zchar_t* webinarToken; // Webinar token
const zchar_t* userZAK; // Zoom Access Key
const zchar_t* app_privilege_token; // App privilege token (for raw data)
const zchar_t* onBehalfToken; // On behalf token
bool isVideoOff; // Start with video off
bool isAudioOff; // Start with audio off
};IZoomSDKRenderer Methods
| Method | Description | Returns |
|---|---|---|
setRawDataResolution(ZoomSDKResolution) | Set resolution | SDKError |
subscribe(uint32_t userId, ZoomSDKRawDataType) | Subscribe to video | SDKError |
unSubscribe() | Unsubscribe | SDKError |
getResolution() | Get resolution | ZoomSDKResolution |
getSubscribeId() | Get user ID | uint32_t |
ZoomSDKResolution values: ZoomSDKResolution_90P, _180P, _360P, _720P, _1080P
YUVRawDataI420 Methods
| Method | Description | Returns |
|---|---|---|
GetStreamWidth() | Video width | uint32_t |
GetStreamHeight() | Video height | uint32_t |
GetYBuffer() | Y plane buffer | char* |
GetUBuffer() | U plane buffer | char* |
GetVBuffer() | V plane buffer | char* |
GetBufferLen() | Total buffer length | uint32_t |
GetRotation() | Rotation angle | int |
GetAlphaBuffer() | Alpha channel | char* |
Video Format:
- YUV420 (I420) contiguous planar format (no strides)
- Y plane:
width * heightbytes - U plane:
(width/2) * (height/2)bytes - V plane:
(width/2) * (height/2)bytes - Total size:
width * height * 1.5bytes
AudioRawData Methods
| Method | Description | Returns |
|---|---|---|
GetBuffer() | Audio buffer | char* |
GetBufferLen() | Buffer length (bytes) | uint32_t |
GetSampleRate() | Sample rate (Hz) | uint32_t |
GetChannelNum() | Number of channels | uint32_t |
Audio Format:
- PCM (uncompressed), 16-bit, little-endian
- Sample rate: 32000 Hz (typical)
- Channels: 1 (mono) or 2 (stereo)
Playing Raw Files with FFmpeg
Raw files have no headers - specify format explicitly:
# Play YUV video (adjust dimensions to match your output)
ffplay -video_size 640x360 -pixel_format yuv420p -f rawvideo video.yuv
# Convert YUV to MP4
ffmpeg -video_size 640x360 -pixel_format yuv420p -f rawvideo -i video.yuv -c:v libx264 output.mp4
# Play PCM audio
ffplay -f s16le -ar 32000 -ac 1 audio.pcm
# Convert PCM to WAV
ffmpeg -f s16le -ar 32000 -ac 1 -i audio.pcm output.wav
# Combine video + audio into MP4
ffmpeg -video_size 640x360 -pixel_format yuv420p -f rawvideo -i video.yuv \
-f s16le -ar 32000 -ac 1 -i audio.pcm \
-c:v libx264 -c:a aac -shortest output.mp4Error Codes (SDKError)
| Code | Description |
|---|---|
SDKERR_SUCCESS | Success |
SDKERR_INVALID_PARAMETER | Invalid parameter |
SDKERR_UNINITIALIZE | SDK not initialized |
SDKERR_UNAUTHENTICATION | Not authenticated |
SDKERR_NO_PERMISSION | No permission |
SDKERR_NO_AUDIODEVICE_ISFOUND | No audio device |
SDKERR_NO_VIDEODEVICE_ISFOUND | No video device |
SDKERR_INTERNAL_ERROR | Internal error |
SDKERR_SERVICE_FAILED | Service failed |
SDKERR_MEMORY_FAILED | Memory allocation failed |
SDKERR_TOO_FREQUENT_CALL | API called too frequently |
Authentication Results (AuthResult)
| Result | Description |
|---|---|
AUTHRET_SUCCESS | Authentication successful |
AUTHRET_KEYORSECRETEMPTY | Key or secret empty |
AUTHRET_JWTTOKENWRONG | JWT token invalid |
AUTHRET_OVERTIME | Operation timed out |
Meeting Status (MeetingStatus)
| Status | Description |
|---|---|
MEETING_STATUS_IDLE | No meeting |
MEETING_STATUS_CONNECTING | Connecting |
MEETING_STATUS_INMEETING | In meeting |
MEETING_STATUS_RECONNECTING | Reconnecting |
MEETING_STATUS_FAILED | Failed |
MEETING_STATUS_ENDED | Meeting ended |
MEETING_STATUS_WAITINGFORHOST | Waiting for host |
Authentication Requirements (2026 Update)
Important: Beginning March 2, 2026, apps joining meetings outside their account must be authorized.
Options:
- App Privilege Token (OBF) - Recommended for bots
- ZAK Token - Zoom Access Key
- On Behalf Token - For specific use cases
Resources
- Official docs: https://developers.zoom.us/docs/meeting-sdk/linux/
- API Reference: https://marketplacefront.zoom.us/sdk/meeting/linux/index.html
- Headless sample: https://github.com/zoom/meetingsdk-headless-linux-sample
- Raw recording sample: https://github.com/zoom/meetingsdk-linux-raw-recording-sample
- Auth endpoint: https://github.com/zoom/meetingsdk-auth-endpoint-sample
Meeting SDK Linux 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for Linux (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/linux/
- https://marketplacefront.zoom.us/sdk/meeting/linux/
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/linux/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/linux/
macOS Architecture
Layer Model
- App shell (AppKit/Swift UI integration layer).
- Meeting coordinator (join/start state machine).
- SDK service/controller layer (ZoomSDK class + service delegates).
- Backend signing/token service.
Reference Flow
macOS App -> Meeting Coordinator -> Backend Signature Service -> Meeting SDK
^ | | |
| v v v
User actions State + retry policy Role/token policy Service delegatesWhy this split
- Isolates security logic from desktop client.
- Makes controller/delegate ordering explicit.
- Reduces upgrade regression blast radius.
macOS Lifecycle Workflow
Core Sequence
1. App startup and SDK initialization. 2. SDK auth callback success. 3. Join/start flow selection. 4. Meeting controller registration and in-meeting feature activation. 5. Leave/end handling. 6. SDK cleanup and process teardown.
Failure Domains
- Auth/signature mismatch.
- Join/start parameter mismatch.
- Delegate/controller ordering issues in custom UI mode.
- Feature-level permission or role mismatch (recording, breakout, webinar).
macOS Join/Start Pattern
Join (attendee)
1. Get short-lived signature from backend. 2. Initialize/auth SDK and verify callback result. 3. Join with meeting number + passcode. 4. Register required meeting delegates before user interaction.
Start (host)
1. Backend provides host ZAK + role-aware signature. 2. Start flow executes with host token. 3. Host-only controls enabled after privilege verification.
Guardrails
- Keep SDK secret off client.
- Validate delegate callbacks under both default and custom UI.
- Explicitly handle leave/end transitions for cleanup.
Meeting SDK macOS Guide
Scope
macOS Meeting SDK integration for default/custom UI, auth, join/start, and in-meeting feature controllers.
Validation Snapshot
- Docs coverage includes: get-started, integrate, start/join/auth paths, default UI, custom UI, service quality, and error-code pages.
- API reference snapshot includes class/protocol maps, globals/functions pages, and file-level references.
- Local package checked:
zoom-sdk-macos-6.7.6.75900withZoomSDKSampleand native macOS app sources.
Practical Guidance
1. Get stable default UI flow first. 2. Add custom UI and advanced controls incrementally. 3. Validate immersive/share/annotation feature paths after each SDK upgrade.
macOS Meeting SDK Environment Variables
| Variable | Required | Purpose | Where to find |
|---|---|---|---|
ZOOM_SDK_KEY | Yes | SDK signing identity | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_SDK_SECRET | Yes | Server-side signing secret | Zoom Marketplace -> Meeting SDK app -> App Credentials |
ZOOM_MEETING_NUMBER | Join/start | Meeting identifier | Zoom invite / web portal / Meetings API |
ZOOM_MEETING_PASSWORD | Conditional | Meeting passcode | Zoom invite details / Meetings API |
ZOOM_ROLE | Yes | Signature role (0 attendee, 1 host) | App business logic |
ZOOM_ZAK | Host start | Host authorization token | Zoom REST API token flow |
Notes
- Keep signing on backend.
- For desktop distribution, keep runtime token handling outside checked-in configs.
macOS Reference Map
Sources
- Docs: https://developers.zoom.us/docs/meeting-sdk/macos/
- API Reference: https://marketplacefront.zoom.us/sdk/meeting/macos/annotated.html
Crawl Coverage Snapshot
- Docs pages captured:
45 - API reference pages captured:
528
Key API Entry Pages
annotated.mdclasses.mdfiles.mdhierarchy.mdfunctions*globals*pages.md
Notable API Surface Areas
- ZoomSDK service/controller interfaces
- Meeting/audio/video/share/webinar/breakout modules
- AI companion, smart summary, and avatar related interfaces
- Raw data helper/delegate interfaces
Drift Signals to Watch
- Frequent growth in
globals*and controller interfaces. - Added AI Companion and smart-summary-related surfaces across recent versions.
macOS Versioning and Compatibility
Observed Versions
- Local SDK package:
v6.7.6.75900 - Docs baseline: current macOS Meeting SDK docs tree captured on this crawl.
Compatibility Practices
- Pin exact SDK package and re-test controller/delegate contracts on upgrade.
- Verify custom UI features (annotation/share/immersive) first during upgrade testing.
- Maintain a release checklist for host-only and webinar-specific flows.
Contradiction/Drift Notes
- Docs contain both top-level and nested advanced-feature paths; treat them as parallel documentation organization, not separate APIs.
- Changelog in package is external-link based; maintain local upgrade notes for exact behavior changes.
Meeting SDK macOS 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for macOS (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/macos/
- https://marketplacefront.zoom.us/sdk/meeting/macos/annotated.html
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/macos/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/macos/
macOS High-Level Scenarios
Scenario 1: Enterprise desktop collaboration app
- Default UI embed for fast rollout.
- Adds policy-based host controls.
- Logs meeting lifecycle and quality stats for support analytics.
Scenario 2: Media-rich custom meeting app
- Uses custom UI for branded layout and controlled tool access.
- Integrates share/annotation/immersive features.
- Keeps default UI fallback for release safety.
Scenario 3: Training operations console
- Hosts meetings with role-aware controls.
- Uses breakout and participant controllers.
- Adds upgrade compatibility checks before broad deployment.
macOS Common Issues
1. Join/start flow errors
- Validate signature freshness and role.
- Confirm meeting identifier and passcode mapping.
- Validate host token (
ZAK) for start path.
2. Delegate callback gaps
- Ensure delegate/controller registration happens before feature usage.
- Keep coordinator/service objects strongly referenced through session lifecycle.
3. Custom UI regressions
- Verify default UI still works to isolate custom layer issues.
- Re-check rendering and feature-controller dependencies after SDK upgrades.
4. Version drift
- Re-run feature-level tests on breakout, share, annotation, and AI companion modules.
- Compare API reference map for renamed or newly required interfaces.
Architecture
The React Native package is a wrapper around native Meeting SDKs.
Layers
- JS API layer:
@zoom/meetingsdk-react-native - Context/hook layer:
ZoomSDKProvider,useZoom - Native module layer:
RNZoomSDK(iOS Obj-C, Android Java) - Zoom native SDK layer: MobileRTC (iOS) and ZoomSDK (Android)
Why this matters
- Wrapper updates can change JS signatures while native SDK versions evolve independently.
- Some options are platform-specific (
logSizeAndroid,bundleResPathiOS). - Numeric error codes from native must be interpreted by platform docs.
Auth and Token Model
Token types used by wrapper
jwtTokenininitSDK: Meeting SDK JWT (for SDK authorization)zoomAccessTokeninstartMeeting: ZAK token for host start
Security model
- Generate tokens server-side only.
- Never ship SDK secret in the app.
- Keep JWT short-lived and rotate aggressively.
Flow guidance
- Participant join:
initSDK(jwtToken)+joinMeeting(meetingNumber, password) - Host start:
initSDK(jwtToken)+startMeeting(zoomAccessToken=ZAK, meetingNumber)
High-Level Scenarios
1. Mobile attendee app (customer-facing)
- User opens iOS/Android app and joins meetings from in-app schedule.
- Backend provides short-lived Meeting SDK JWT.
- App calls
joinMeetingwith meeting number and passcode when required.
2. Mobile host operations app
- Authenticated operator starts scheduled sessions from mobile.
- Backend retrieves host ZAK via Zoom APIs.
- App uses
startMeetingwithzoomAccessToken.
3. Kiosk-style controlled join flow
- App runs in constrained device mode.
- Meeting controls are reduced via meeting flags/settings.
- App enforces deterministic init -> join -> cleanup sequence on each session.
4. Support/field workforce app
- Agents join support calls and optionally use share/chat controls.
- Per-platform settings are tuned independently (Android/iOS parity is not guaranteed).
- Runtime fallback handling is added for unsupported feature flags.
Lifecycle Workflow
Recommended runtime flow:
1. App bootstraps and wraps tree with ZoomSDKProvider. 2. initSDK runs once with jwtToken, domain, logging options. 3. App checks isInitialized() before meeting actions. 4. User chooses:
joinMeeting(participant)startMeeting(host, with ZAK)
5. Native Meeting SDK UI/session runs. 6. Call cleanup() on app shutdown/logout.
React UI -> ZoomSDKProvider -> JS Wrapper (ZoomSDK.ts)
-> Native Bridge (RNZoomSDK)
-> iOS MobileRTC / Android ZoomSDKIf initialization/auth fails, stop and rotate token before retrying.
Join Meeting Pattern
import { useZoom } from '@zoom/meetingsdk-react-native';
const zoom = useZoom();
await zoom.joinMeeting({
userName: 'participant-name',
meetingNumber: '123456789',
password: 'meeting-password',
userType: 1,
});Notes:
meetingNumberanduserNameare required by wrapper validation.passwordis optional in API shape, but may be mandatory by meeting settings.
Provider Hook Pattern
Use wrapper context consistently.
import { ZoomSDKProvider, useZoom } from '@zoom/meetingsdk-react-native';
function MeetingActions() {
const zoom = useZoom();
// zoom.joinMeeting / zoom.startMeeting / zoom.cleanup
}Do not call wrapper methods before provider initialization is complete.
Setup Guide
1. Install package
npm install @zoom/meetingsdk-react-native2. Respect documented support boundaries
- React Native support currently documented up to
0.75.4. - Expo is currently not supported.
- Android baseline from docs:
minSdkVersion = 26,targetSdkVersion = 35.
3. Align platform SDK versions
- This wrapper does not bundle native iOS/Android Meeting SDK artifacts for all workflows.
- Keep wrapper and native Meeting SDK versions aligned.
- For older wrapper versions (pre-
6.4.5), docs note manual native SDK placement may be required.
4. Initialize provider
import { ZoomSDKProvider } from '@zoom/meetingsdk-react-native';
<ZoomSDKProvider
config={{
jwtToken: '<MEETING_SDK_JWT>',
domain: 'zoom.us',
enableLog: true,
logSize: 5,
}}
>
<App />
</ZoomSDKProvider>5. Platform prerequisites
- Android: include Zoom Meeting SDK dependency in gradle and required permissions.
- iOS: ensure Podfile and framework setup are aligned with package expectations.
See Android Setup and iOS Setup.
Start Meeting Pattern
import { useZoom } from '@zoom/meetingsdk-react-native';
const zoom = useZoom();
await zoom.startMeeting({
userName: 'host-name',
meetingNumber: '123456789',
zoomAccessToken: '<ZAK>',
});Notes:
zoomAccessTokenis required for host start in wrapper validation.- Missing/expired ZAK returns native start failure code.
Android Setup Notes
Representative dependency from SDK package example:
implementation('us.zoom.meetingsdk:zoomsdk:6.7.2')Other observed setup details:
- Java/Kotlin target 17 in sample.
- Wrapper maps many
JoinMeetingOptions/StartMeetingOptionsflags. languagesetting is consumed by native bridge duringupdateMeetingSetting.
Always verify against your app's RN/Gradle/Kotlin compatibility matrix.
iOS Setup Notes
Observed in package sample project:
- Podfile includes React Native integration and required permissions pods.
- Wrapper supports optional init fields:
bundleResPathappGroupIdreplaykitBundleIdentifier
These are relevant for custom resource path and screen-share style setups.
Confirm iOS deployment target and Podfile settings against your RN version.
Native Bridge Notes
Android bridge
- Uses
ZoomSDK.initialize(...)withwrapperType = 2. joinMeetingandstartMeetingresolve numeric result codes.- Exposes lifecycle hooks but event emitter list is currently empty in bridge.
iOS bridge
- Initializes
MobileRTC+ auth service (sdkAuthwith JWT). joinMeeting/startMeetingcall native meeting service methods and resolve/reject promises.
Practical implication
The wrapper currently behaves as command-based API with limited cross-platform event exposure. Build app-level state handling around command results and native UI transitions.
Official Sources
Primary sources used for this skill:
- Zoom docs (React Native Meeting SDK): https://developers.zoom.us/docs/meeting-sdk/react-native/
- Zoom reference (React Native modules): https://marketplacefront.zoom.us/sdk/meeting/reactnative/modules.html
- Zoom Meeting SDK React Native package 6.7.2 (local archive analysis)
- Crawled docs snapshots under
skills/raw-docs/developers.zoom.us/docs/meeting-sdk/react-native/ - Crawled reference snapshots under
skills/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/reactnative/
Related:
- Meeting SDK auth: https://developers.zoom.us/docs/meeting-sdk/auth/
- Quickstart repo: https://github.com/zoom/MeetingSDK-ReactNative-Quickstart
Wrapper API Reference (React Native package)
Init config
jwtToken?: stringdomain?: stringenableLog?: booleanlogSize?: number(Android)bundleResPath?: string(iOS)appGroupId?: string(iOS)replaykitBundleIdentifier?: string(iOS)
Methods
initSDK(config): Promise<boolean>isInitialized(): Promise<boolean>joinMeeting(config): Promise<number>startMeeting(config): Promise<number>updateMeetingSetting(config): voidcleanup(): void
Join config highlights
- Required:
userName,meetingNumber - Optional:
password,zoomAccessToken,vanityID,webinarToken,joinToken,appPrivilegeToken
Start config highlights
- Required:
userName,zoomAccessToken - Optional:
meetingNumber,vanityID,inviteContactId
Meeting SDK React Native 5-Minute Preflight Runbook
Use this before deep debugging.
Skill Doc Standard Note
- Skill entrypoint is
SKILL.md. - This runbook is an operational convention (recommended), not a required skill file.
- SDK/API names can drift by version; validate current names against docs/raw-docs before release.
1) Confirm Integration Surface
- Confirm this is a Meeting SDK embed path for React Native (not REST
join_urlonly). - Choose default/full UI first, then move to custom UI after stable join/start.
- Wrapper platforms (Web/React Native/Electron) require extra runtime and bridge checks.
2) Confirm Required Credentials
- Meeting SDK app credentials (Client ID/Secret).
- Backend-generated Meeting SDK signature/JWT.
- Meeting identifiers (
meetingNumber, password) and ZAK for host start flows when needed.
3) Confirm Lifecycle Order
1. Initialize SDK and register event handlers. 2. Authenticate SDK session/token. 3. Join or start meeting/webinar with role-appropriate credentials. 4. Handle in-meeting events and network/media state updates.
4) Confirm Event/State Handling
- Correlate meeting/session state changes with participant identity and role.
- Handle reconnect/waiting-room transitions explicitly.
- Keep callback/promise/event handlers idempotent to avoid duplicate actions.
5) Confirm Cleanup + Upgrade Posture
- Leave meeting and release SDK resources cleanly.
- Remove listeners/subscriptions during component/app teardown.
- Re-check quarterly version enforcement windows before release updates.
6) Quick Probes
- Init/auth succeeds before join/start attempt.
- Join/start flow completes once on target platform without stale state.
- Core media controls (audio/video/share) respond to expected events.
7) Fast Decision Tree
- 401/signature errors -> backend signature claims/time skew/app credentials mismatch.
- UI loads but cannot join -> wrong role/ZAK/password field or invalid meeting data.
- Random event behavior -> listeners attached multiple times or detached too early.
8) Source Checkpoints
Official docs
- https://developers.zoom.us/docs/meeting-sdk/react-native/
- https://marketplacefront.zoom.us/sdk/meeting/reactnative/modules.html
Raw docs in repo
raw-docs/developers.zoom.us/docs/meeting-sdk/react-native/raw-docs/marketplacefront.zoom.us/sdk/meeting-sdk/reactnative/
Common Issues
joinMeeting fails immediately
- Validate meeting number format and password.
- Confirm SDK initialization succeeded first.
- Check JWT validity window.
startMeeting fails
- Verify
zoomAccessToken(ZAK) is present and unexpired. - Ensure host account and meeting ownership match ZAK context.
Provider/hook misuse
- Ensure components calling
useZoom()are wrapped inZoomSDKProvider.
iOS-specific init issues
- Confirm optional fields (
bundleResPath,appGroupId,replaykitBundleIdentifier) only when needed and correctly configured.
Android language crash risk
- Avoid passing partial/invalid language values to
updateMeetingSetting.
Meeting SDK (macOS) Pointers
Stable pointer for macOS Meeting SDK questions.
Start here:
- ../macos/SKILL.md
Common question patterns:
- How do I choose default UI vs custom UI?
- How do I handle start/join flows with host privileges?
- Which service/controller delegates must be registered first?
Practical guidance:
- Validate default UI join path before custom UI.
- Confirm auth/signature and role alignment before host actions.
- Use ../macos/references/macos-reference-map.md for API discovery and upgrade drift checks.
Related skills
How it compares
Use for embedded Android Meeting SDK apps instead of Zoom OAuth-only or web SDK skills.
FAQ
What does build-zoom-meeting-sdk-app do?
Reference skill for Zoom Meeting SDK. Use after routing to a meeting-embed workflow when implementing real Zoom meeting joins, platform-specific SDK behavior, auth and join.
When should I use build-zoom-meeting-sdk-app?
User asks about build zoom meeting sdk app or related SKILL.md workflows.
Is build-zoom-meeting-sdk-app safe to install?
Review the Security Audits panel on this page before installing in production.