Verify signatures#
This page explains how to verify that a webhook request was sent by ALPHA.
Every delivery is an HTTPS POST signed with HMAC-SHA256 (scheme
v1). Always verify using the raw request body before you parse JSON.
Important
If verification fails, return 401 or 403 and do not process the body.
Request headers#
|
Header |
Type |
Description |
|---|---|---|
|
|
string |
Event identifier (same as JSON |
|
|
integer |
Unix time in seconds when this delivery attempt was signed. |
|
|
string |
Space-separated signature entries. ALPHA sends |
Endpoint secret#
The secret format is whsec_<base64>. Remove the whsec_ prefix,
base64-decode the remainder, and use those bytes as the HMAC key.
How to verify#
Build the signed string from the same request you received:
{webhook-id}.{webhook-timestamp}.{raw_body}
Then:
-
Compute
HMAC-SHA256over the UTF-8 bytes of that string. -
Base64-encode the 32-byte digest.
-
Compare
v1,<base64>to an entry inwebhook-signaturewith a constant-time comparison. -
Reject the request if
webhook-timestampis outside a small window (for example ±5 minutes) to limit replay attacks.
Warning
ALPHA serializes JSON with sorted keys and compact separators
(",", ":"). Re-serializing a parsed object usually breaks the
signature. Always use the raw HTTP body bytes.
Example implementation (Python)#
import base64
import hashlib
import hmac
import time
def verify(headers, raw_body: bytes, secret: str, tolerance: int = 300) -> bool:
# secret like "whsec_..."
key = base64.b64decode(secret.removeprefix("whsec_"))
msg_id = headers["webhook-id"]
ts = int(headers["webhook-timestamp"])
if abs(time.time() - ts) > tolerance:
return False
signed = f"{msg_id}.{ts}.{raw_body.decode('utf-8')}".encode("utf-8")
digest = hmac.new(key, signed, hashlib.sha256).digest()
expected = "v1," + base64.b64encode(digest).decode("ascii")
candidates = headers["webhook-signature"].split()
return any(hmac.compare_digest(expected, c) for c in candidates)
See also#
Event envelope and sample payloads.
Retry behavior and HTTP response handling.