Verify Zendesk Webhook Signatures
Zendesk signs every webhook with HMAC-SHA256 over the delivery timestamp concatenated with the raw body, base64-encodes it and sends it in the X-Zendesk-Webhook-Signature header, with the timestamp in X-Zendesk-Webhook-Signature-Timestamp. Paste the raw body, the timestamp header value and your signing secret below.
Paste the raw request body exactly as received. Some Zendesk deliveries (e.g. test pings) have an empty body — the timestamp alone is signed then.
Everything runs in your browser — the payload and secret never leave this page. Want to verify a different provider? See the webhook signature verifier hub or the generic HMAC generator.
How Zendesk signs webhooks
- Read
X-Zendesk-Webhook-Signature(the signature) andX-Zendesk-Webhook-Signature-Timestamp(an ISO-8601 timestamp) from the request. - Build the signed string: the timestamp value immediately followed by the raw request body (no separator).
- Compute HMAC-SHA256 of that string with your webhook signing secret as the key, then base64-encode the digest.
- Compare it to the header with a constant-time check, and reject deliveries whose timestamp is too old to defend against replays.
Verify Zendesk signatures in code
const crypto = require('crypto');
// body must be the RAW request body (string/Buffer), not parsed JSON.
function isValidZendeskWebhook(body, signature, timestamp, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(timestamp + body)
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(signature));
}
const ok = isValidZendeskWebhook(
rawBody,
req.headers['x-zendesk-webhook-signature'],
req.headers['x-zendesk-webhook-signature-timestamp'],
process.env.ZENDESK_SIGNING_SECRET);import base64, hashlib, hmac
def is_valid_zendesk_webhook(body: bytes, signature: str,
timestamp: str, secret: str) -> bool:
digest = hmac.new(secret.encode(),
timestamp.encode() + body,
hashlib.sha256).digest()
expected = base64.b64encode(digest).decode()
return hmac.compare_digest(expected, signature)Frequently asked questions
Where do I find the Zendesk webhook signing secret?
In Admin Center open the webhook's details page and click Reveal secret, or call GET /api/v2/webhooks/{webhook_id}/signing_secret. Each webhook has its own secret.
What exactly does Zendesk sign?
The value of X-Zendesk-Webhook-Signature-Timestamp concatenated directly with the raw request body — no separator between them. Requests without a body (some test deliveries) sign the timestamp alone.
Why does my verification fail with the right secret?
Usually the body was re-serialized before hashing (parse-then-stringify changes bytes), or the timestamp header was omitted from the signed string. Always verify against the raw bytes Zendesk sent, prefixed with the timestamp value.
Verify other providers
Receiving Zendesk webhooks on a server behind a firewall or on localhost? Webhook Relay can forward them to your internal service and even verify or transform them before delivery.
