A fully self-contained Python microservice wrapper around the Zoom Rooms C++ SDK. Automatically downloads the SDK and exposes all functionality via REST API.
- ✅ Zero manual setup - SDK downloaded automatically during build
- ✅ Git-friendly - Binaries excluded, only source committed
- ✅ Docker ready - Single command deployment
- ✅ Always latest - Downloads current SDK version from Zoom
- ✅ Persistent data - Paired room credentials survive container updates
The SDK stores paired room credentials in /root/.zoom/data/third_zrc_data.db. This data MUST be persisted across container updates, otherwise all rooms need to be re-paired.
The docker-compose.yml already handles this via a named Docker volume (zrc-data). Room data automatically persists through container recreations and image updates.
Backup your data:
./backup.sh # Creates backups/zrc-data-YYYYMMDD_HHMMSS.tar.gzSee DATA_PERSISTENCE.md for complete backup/restore guide.
┌─────────────────────────────────────┐
│ Your Web Application │
└─────────────┬───────────────────────┘
│ HTTP REST + WebSocket
▼
┌─────────────────────────────────────┐
│ FastAPI Microservice (Python) │
│ - Multi-room state management │
│ - Event broadcasting │
│ - HeartBeat timer │
└─────────────┬───────────────────────┘
│ Python bindings
▼
┌─────────────────────────────────────┐
│ pybind11 C++ Bindings │
│ (auto-generated) │
└─────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Zoom Rooms C++ SDK │
└─────────────────────────────────────┘
- Thinnest possible wrapper - Direct 1:1 mapping of SDK methods to REST endpoints
- Auto-generated bindings - Easy to update when new SDK versions are released
- Multi-room support - Control multiple Zoom Rooms simultaneously
- Real-time events - WebSocket channels for SDK callbacks
- Per-room state management - Each room maintains independent state
- No authentication layer - Delegates auth to your main web service
wrapper/
├── .gitignore # Excludes SDK and binaries
├── bindings/ # C++ pybind11 bindings
│ └── zrc_bindings.cpp # Hand-crafted bindings
├── service/ # FastAPI microservice
│ └── app.py # Main service implementation
├── CMakeLists.txt # Build configuration
├── Dockerfile # Self-contained Docker build
├── docker-compose.yml # Service orchestration
├── requirements.txt # Python dependencies
├── build.sh # Build script with auto SDK download
├── run_service.sh # Service launcher
└── *.md # Documentation
wrapper/
├── Demo/ # SDK demo files (downloaded)
├── include/ # SDK headers (downloaded)
├── libs/ # SDK shared libraries (downloaded)
├── build/ # Build artifacts
├── .venv/ # Python virtual environment
└── service/zrc_sdk*.so # Compiled Python module
All SDK files and binaries are automatically downloaded/generated and excluded from version control.
cd wrapper
# Build and start (automatically downloads SDK)
docker-compose up -d
# Test the API
curl http://localhost:8000/health- API documentation: http://localhost:8000/docs
- Logs:
docker-compose logs -f - Stop:
docker-compose down
cd wrapper
# Build (automatically downloads SDK from Zoom)
./build.sh
# Run the microservice
./run_service.shThe service will start on http://localhost:8000
What happens during build:
- Downloads Zoom Rooms SDK (~31 MB) from Zoom servers
- Extracts SDK files (Demo/, include/, libs/)
- Downloads pybind11 (if needed)
- Compiles the C++ bindings
- Installs the
zrc_sdkPython module toservice/
See SELF_CONTAINED_SETUP.md for complete setup guide.
curl -X POST http://localhost:8000/api/rooms/room1/pair \
-H "Content-Type: application/json" \
-d '{"activation_code": "123-456-789"}'Response:
{
"room_id": "room1",
"result": 0,
"success": true
}curl -X POST http://localhost:8000/api/rooms/room1/meeting/start_instantcurl -X POST http://localhost:8000/api/rooms/room1/meeting/join \
-H "Content-Type: application/json" \
-d '{
"meeting_number": "123456789",
"password": "optional"
}'# Mute
curl -X POST "http://localhost:8000/api/rooms/room1/audio/mute?mute=true"
# Unmute
curl -X POST "http://localhost:8000/api/rooms/room1/audio/mute?mute=false"curl -X POST "http://localhost:8000/api/rooms/room1/video/mute?mute=true"curl -X POST http://localhost:8000/api/rooms/room1/meeting/exitcurl http://localhost:8000/api/roomsConnect to WebSocket for real-time SDK event notifications:
const ws = new WebSocket('ws://localhost:8000/api/rooms/room1/events');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Event received:', data);
};
// Example events:
// {"event": "OnPairRoomResult", "result": 0}
// {"event": "OnUpdateMeetingStatus", "status": "InMeeting"}
// {"event": "OnZRConnectionStateChanged", "state": "ConnectionStateConnected"}
// {"event": "OnConfReadyNotification"}
// {"event": "OnExitMeetingNotification"}import asyncio
import websockets
import json
async def listen_to_room_events():
uri = "ws://localhost:8000/api/rooms/room1/events"
async with websockets.connect(uri) as websocket:
print("Connected to room events")
async for message in websocket:
event = json.loads(message)
print(f"Event: {event}")
asyncio.run(listen_to_room_events())| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Health check |
| GET | /api/rooms |
List all paired rooms |
| POST | /api/rooms/{room_id}/pair |
Pair a room with activation code |
| POST | /api/rooms/{room_id}/unpair |
Unpair a room |
| POST | /api/rooms/{room_id}/meeting/start_instant |
Start instant meeting |
| POST | /api/rooms/{room_id}/meeting/join |
Join meeting by number |
| POST | /api/rooms/{room_id}/meeting/exit |
Exit meeting |
| POST | /api/rooms/{room_id}/audio/mute |
Mute/unmute audio |
| POST | /api/rooms/{room_id}/video/mute |
Mute/unmute video |
| WS | /api/rooms/{room_id}/events |
WebSocket event stream |
See full interactive API docs at http://localhost:8000/docs
When a new Zoom Rooms SDK version is released:
-
Replace SDK files:
# Backup old SDK mv ../include ../include.old mv ../libs ../libs.old # Copy new SDK cp -r /path/to/new/sdk/include ../include cp -r /path/to/new/sdk/libs ../libs
-
Run the update script:
cd wrapper ./update_sdk.sh
This will:
- Regenerate pybind11 bindings
- Rebuild the C++ module
- Verify installation
- Restart the service:
./run_service.sh
To expose additional SDK methods:
- Edit
generator/simple_generator.py - Add methods to the
SDK_CONFIGdictionary - Regenerate bindings:
.venv/bin/python generator/simple_generator.py ./build.sh
- Add corresponding endpoints in
service/app.py
# In generator/simple_generator.py
SDK_CONFIG = {
'IMeetingService': {
'type': 'interface',
'methods': [
# ... existing methods ...
'GetCurrentMeetingInfo() -> MeetingInfo', # Add this
],
},
}Then in service/app.py:
@app.get("/api/rooms/{room_id}/meeting/info")
async def get_meeting_info(room_id: str):
room_service = room_manager.get_room_service(room_id)
if not room_service:
raise HTTPException(status_code=404, detail="Room not found")
meeting_service = room_service.GetMeetingService()
info = meeting_service.GetCurrentMeetingInfo()
return {"meeting_info": info}- Python 3.9+
- CMake 3.12+
- C++14 compiler (gcc/clang)
- Zoom Rooms C++ SDK
cd wrapper
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install Python dependencies
pip install -r requirements.txt
# Generate bindings
python generator/simple_generator.py
# Build C++ module
mkdir -p build && cd build
cmake ..
make -j$(nproc)
make install
# Run service
cd ../service
python app.py# Start the service
./run_service.sh
# In another terminal, test endpoints
curl http://localhost:8000/
curl -X POST http://localhost:8000/api/rooms/test/pair \
-H "Content-Type: application/json" \
-d '{"activation_code": "test"}'Each Zoom Room gets:
- Independent
IZoomRoomsServiceinstance - Separate callback handlers
- Dedicated WebSocket broadcast list
- Isolated state (connection, meeting status)
The SDK requires HeartBeat() to be called every ~150ms on Linux. The microservice:
- Runs a single asyncio task for all rooms
- Calls SDK HeartBeat in a loop
- Starts on service startup
- Stops on shutdown
SDK callbacks (C++) are translated to WebSocket events (JSON):
// C++ SDK callback
void OnPairRoomResult(int32_t result) {
// Handled by pybind11 trampoline
}# Python callback implementation
def OnPairRoomResult(self, result: int):
# Broadcast to all WebSocket clients
broadcast_event(room_id, {
"event": "OnPairRoomResult",
"result": result
})// Client receives
ws.onmessage = (event) => {
// {"event": "OnPairRoomResult", "result": 0}
}Run ./build.sh to compile the C++ module.
Set LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=/home/steve/projects/zoom/libs:$LD_LIBRARY_PATH
./run_service.shOr update CMakeLists.txt RPATH settings.
Ensure the service is running (not crashed). The HeartBeat loop runs automatically in the background.
Check logs:
INFO:root:Starting SDK HeartBeat loop...
Ensure the room exists (pair it first):
curl -X POST http://localhost:8000/api/rooms/room1/pair \
-H "Content-Type: application/json" \
-d '{"activation_code": "..."}'Then connect:
const ws = new WebSocket('ws://localhost:8000/api/rooms/room1/events');This wrapper is provided as-is. Zoom Rooms SDK is subject to Zoom's licensing terms.
For SDK-specific questions, refer to the Zoom Rooms SDK documentation.
For wrapper issues, check:
- Build succeeded:
ls service/zrc_sdk*.so - Service running:
curl http://localhost:8000/ - Logs: Check console output
Generated wrapper components:
- C++ bindings:
bindings/zrc_bindings.cpp(auto-generated) - Python service:
service/app.py - Generator:
generator/simple_generator.py