The Acuity webhook documentation describes the steps to verify the source of the webhook is from Acuity.
First compute the base64
HMAC-SHA256
signature of the notification using the request's body as the message and your API key as the shared secret. Then compare this signature to the request headerX-Acuity-Signature
. If they match, the notification is authentic.
And then it provides some code samples for PHP, JavaScript, and Ruby. These samples help clarify a bit, but use variables without describing their source, leaving me to guess what they refer to. I haven't been able to verify messages.
Here's the code I'm using:
acuity_api_key = 'redacted'
key_bytes = acuity_api_key.encode('utf-8')
signature = request.headers['x-acuity-signature']
message = request.body
message_bytes = message.encode('utf-8')
signed_body = base64.b64encode(
hmac.new(key_bytes, message_bytes, hashlib.sha256).digest()
).decode('utf-8')
if signature != signed_body:
raise RuntimeError('Failed to validate message signature')
I actually had 2 problems.
acuity_api_key = 'redacted'
key_bytes = acuity_api_key.encode('utf-8')
signature = event['headers']['x-acuity-signature']
encoded_message = event['body']
if event.get('isBase64Encoded', True):
decoded_message_bytes = base64.b64decode(encoded_message)
else:
decoded_message_bytes = encoded_message.encode('utf-8')
signed_body = base64.b64encode(
hmac.new(key_bytes, decoded_message_bytes, hashlib.sha256).digest()
).decode('utf-8')
if signature != signed_body:
raise RuntimeError('Failed to validate message signature')
The API key I was using for my personal Squarespace account, and I was relying on the 'static webhook' configuration. It is documented that it uses the "main admin's API key". I had assumed the API key displayed in the Squarespace scheduling's 'Integrations' tab was admin key for the integration (vs my API key from some OAuth flow). Actually, Squarespace users see different API keys when they visit the Integrations page. I set up "Dynamic Webhooks", and used my own API key instead. I expect the Squarespace "owner" account API key would have worked.
I continued to have problems with order.completed
hook verification. Despite having signed up for the hook with my (non-owner) credentials, the hook was still being signed with the credentials from the Squarespace owner account for the site. I've opened a support request about that particular issue.