Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions examples/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ For the specific event names for your product, see the [product guides](../READM
import rtms
import hmac
import hashlib
import json
import os

def verify_signature(body: str, timestamp: str, signature: str) -> bool:
def verify_signature(payload: dict, timestamp: str, signature: str) -> bool:
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
message = f"v0:{timestamp}:{body}"
expected = "v0=" + hmac.new(
os.environ["ZM_RTMS_WEBHOOK_SECRET"].encode(),
Expand All @@ -61,7 +63,7 @@ def handle_webhook(payload, request, response):
signature = request.headers.get('x-zm-signature', '')
timestamp = request.headers.get('x-zm-request-timestamp', '')

if not verify_signature(str(payload), timestamp, signature):
if not verify_signature(payload, timestamp, signature):
response.set_status(401)
response.send({'error': 'Unauthorized'})
return
Expand All @@ -78,6 +80,8 @@ def handle_webhook(payload, request, response):
rtms.run()
```

Zoom signs the canonical JSON body, the same string JavaScript produces with `JSON.stringify(payload)`. The two `json.dumps` flags reproduce that exact string: `separators=(",", ":")` drops the whitespace Python adds after `:` and `,`, and `ensure_ascii=False` keeps non-ASCII characters as literals instead of `\uXXXX` escapes. Passing `str(payload)` instead hashes Python's `dict` repr (single quotes), so the HMAC never matches and every webhook is rejected with a 401.

## Context Manager

Use `with rtms.Client() as client:` to ensure `leave()` is always called — even if an exception occurs:
Expand Down