Validation of webhook endpoint url not working

Hi there!
I’ve recently migrated to s2s-oauth as a replacement to my jwt app, and I’m now at the point of adding webhook notifications.
I’ve got my endpoint set up properly I think (didn’t actually change anything from the working jwt endpoint code), but when I input the endpoint-url in the app definitions and click “validate” the validation fails.
I looked at the logs of my endpoint, and I see the post-request, I see the 200 response, and things seem to be working, but the validation step still fails…
What am I doing wrong?

(For what it’s worth, my endpoint is on pythonanywhere and is implemented using flask).


@shlomi.fenster Hope you will be fine.

Here is the session on how to enable Zoom Webhook.

I don’t understand. I need to use the secret token thing now? The jwt app didn’t have that, so this is new to me.
Is there a python example as to what I should be doing on my endpoint flask app?

Do I understand correctly that using the x-zm-signature and the secret token stuff are something that I can optionally use in order to validate that the request indeed came from my zoom-app-webhook?
Why would “skipping” this stage and just returning a 200-status response culminate in the validation failing? How would my s2s-oauth app even know if I checked the x-zm-signature??
I think the issue is something else.
Again - I recieve the post-request at my endpoint url, and do whatever it is I want to do (save a json of the request data or something) and then return a flask.Response(200).
Why would this fail? Even removing the middle part and just immediatly returning a 200-response is failing validation…

Here, the most simple form of failure is even doing this:

from flask import Flask, request, Response

app = Flask(__name__)

@app.route('/new_zoom_recording_available/', methods=['POST'])
def run_main_new_zoom_recording_available():
    return Response(status=200)

@shlomi.fenster DM me.

This is embarrasing, but how do I send a direct message?? :man_facepalming: :joy:

Um, anyone? What am I missing?

Please ping/DM me via my WhatsApp you will get that by clicking my profile image.

Ok, I’ve made some progress, reread the documentation, and I’ve tried implementing the necessary code, but I’m doing something wrong because my calculation of the x-zm-signature is not managing to recreate the x-zm-signature in the request.
Here’s (a snippet of ) my code, please tell me what I’m doing wrong:

request_body = request.get_json(force=True)

x_zm_signature = request.headers['x-zm-signature']

message = f"v0:{request.headers['x-zm-request-timestamp']}:{json.dumps(request_body)}"
hashed_message =, 'latin-1'),
                          msg=bytes(message, 'latin-1'),
signature = f"v0={hashed_message}"

# But signature != x_zm_signature :-(


Can you try this? I feel like json.dumps need seperators param and ensure_ascii flag false. We ran into an issue with spanish accent characters in the payload. ensure_ascii=false fixed that issue.

x_zm_signature = headers['X-Zm-Signature']
x_zm_request_timestamp = request.headers['X-Zm-Request-Timestamp']

json_request = json.dumps(
    request_body, separators=(",", ":"), ensure_ascii=False)

secret_token = bytes(ZOOM_SECRET_TOKEN, 'utf-8')

message = "v0:{}:{}".format(
    x_zm_request_timestamp, json_request).encode('utf-8')

hashed_message =
    secret_token, msg=message,

signature = "v0={}".format(hashed_message)

# Signatures mismatch check
if x_zm_signature != signature:
    # stuff after signature mismatch
1 Like

Yes!!! This did it! Thank you very very much!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.