Signature Verification
Verify that webhook POSTs originated from ChatYug before processing the JSON body.
When a signature is sent
If you configured a secret key in Webhook Settings, ChatYug includes:
X-Chatyug-Signature: sha256=<hex_digest>
If no secret is configured, the signature header is omitted.
Algorithm
- Read the raw request body as UTF-8 text (before JSON parsing).
- Compute HMAC-SHA256 of that string using your webhook secret as the key.
- Hex-encode the digest and prefix with
sha256=. - Compare with the
X-Chatyug-Signatureheader using a constant-time comparison.
Additional header
X-Chatyug-Event-Id duplicates the eventId field in the JSON body. Use it for logging and idempotency checks.
Verification examples
const crypto = require('crypto');
// Use express.raw({ type: 'application/json' }) on the webhook route
function verifyChatyugWebhook(rawBody, signatureHeader, secret) {
if (!secret) return true;
if (!signatureHeader) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}import hmac
import hashlib
def verify_chatyug_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
if not secret:
return True
if not signature_header:
return False
expected = 'sha256=' + hmac.new(
secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)function verifyChatyugWebhook(string $rawBody, ?string $signature, ?string $secret): bool {
if (!$secret) return true;
if (!$signature) return false;
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}
Important: Verify the signature against the raw body bytes. If your framework parses JSON first, the re-serialized string may not match and verification will fail.
Configure your secret in Webhook Settings (login required). The secret is separate from your API key — see Authentication.