Webhook HMAC verification
Every outbound webhook delivery from Chatevo is signed with HMAC-SHA256 so your endpoint can verify authenticity before processing.
Headers
Section titled “Headers”Each POST to your webhook URL includes:
| Header | Description |
|---|---|
X-Chatevo-Signature | Hex-encoded HMAC-SHA256 signature |
X-Chatevo-Timestamp | Unix timestamp (seconds) when the payload was signed |
X-Chatevo-Event | Event type, e.g. message.created |
Content-Type | application/json |
Signing algorithm
Section titled “Signing algorithm”The signed string is:
{timestamp}.{raw_request_body}HMAC key: your endpoint’s signing_secret (returned once at webhook creation).
signature = HMAC_SHA256(signing_secret, "{timestamp}.{body}")Verification steps
Section titled “Verification steps”- Read the raw request body (before JSON parsing).
- Read
X-Chatevo-Timestampand reject if older than 5 minutes (replay protection). - Compute expected signature from
timestamp + "." + body. - Compare with
X-Chatevo-Signatureusing constant-time comparison. - Process the event only if signatures match.
Node.js example
Section titled “Node.js example”const crypto = require("crypto");
function verifyChatevoWebhook(req, signingSecret) { const signature = req.headers["x-chatevo-signature"]; const timestamp = req.headers["x-chatevo-timestamp"]; const body = req.rawBody; // use raw bytes, not re-serialized JSON
const age = Math.abs(Date.now() / 1000 - Number(timestamp)); if (age > 300) return false;
const expected = crypto .createHmac("sha256", signingSecret) .update(`${timestamp}.${body}`) .digest("hex");
return crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex") );}Python example
Section titled “Python example”import hmacimport hashlibimport time
def verify(signing_secret: str, timestamp: str, body: bytes, signature: str) -> bool: if abs(time.time() - int(timestamp)) > 300: return False payload = f"{timestamp}.".encode() + body expected = hmac.new( signing_secret.encode(), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, signature)Rotating secrets
Section titled “Rotating secrets”- Create a new webhook endpoint (or PATCH to rotate secret).
- Update your verifier with the new secret.
- Delete the old endpoint after traffic drains.
Failed verification
Section titled “Failed verification”Log and return 401 — do not process the payload. Legitimate retries use the same signing scheme.