We’re trying to harden our webhook consumer in Berlin by verifying the X-Genesys-Signature header to prevent replay attacks. The docs say the signature is an HMAC-SHA256 of the payload combined with the timestamp. We’re using the Python hmac library to recreate it, but the comparison always fails.
Here’s the snippet we’re running:
import hmac
import hashlib
secret = 'our_webhook_secret_key'
body = request.data
timestamp = request.headers.get('X-Genesys-Timestamp')
header_sig = request.headers.get('X-Genesys-Signature')
# Docs imply it's just body + timestamp, but order matters
message = (body + timestamp).encode('utf-8')
calculated_sig = hmac.new(secret.encode('utf-8'), message, hashlib.sha256).hexdigest()
if calculated_sig != header_sig:
print('Invalid signature')
The calculated_sig never matches header_sig. We’ve tried swapping the order of body and timestamp, adding a colon separator, and even URL-encoding the body, but nothing works. The timestamp header looks like a standard epoch string. We’re getting 200 OK responses from Genesys, so the delivery is fine, but our local validation is rejecting everything.
Is the payload raw bytes or the JSON string? The docs are vague on whether to include the BOM or newlines. We’re logging the raw request.data and it looks clean. Maybe the secret needs base64 decoding first? We’re stuck on this for two days. The config is fine, it’s just this auth check blocking us from processing the events. Any working examples in Python or Node would help. We need to know exactly what string is being hashed before the HMAC.