Introduction

Webhooks provide an additional security measure to verify that the webhook is genuine and has come from Zendesk. This can be useful if you're looking to ensure only Zendesk webhooks are being made to your endpoint and ensure the information is genuine and as the system expects. These signatures will also help prevent against replay attacks. Verifying the signing secret is optional.

Webhook requests will contain two headers which can be used to verify the request's authenticity:

  • X-Zendesk-Webhook-Signature - the main signature
  • X-Zendesk-Webhook-Signature-Timestamp - the timestamp used to verify the signature

This is used in conjunction with the payload of the request (if there is one).

Verifying the signature

Sign the body and signature timestamp with the webhook secret key using SHA256, then base64 encoding the resulting digest.

Represented simply: base64(HMACSHA256(TIMESTAMP + BODY))

To verify the signature, create the same SHA256 HMAC signature and then compare it to the webhook payload to ensure that they match. If they match, then you can be sure that the webhook came from Zendesk. If they don't, it may be a request from another source.

Not all requests from webhooks will have a body (GETs, DELETEs), so ensure that this scenario is accounted for in any verification code. Depending on language, this may be an empty string or null. Consult your language's documentation for details.

NodeJS verification example

The example below shows how a signature can be verified using NodeJS and Express. In a production environment, this check should form part of the check to accept or reject the webhook request.

const express = require("express");const crypto = require("crypto");require("body-parser-xml")(express);
// Signing secret from webhook itselfconst SIGNING_SECRET = "dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==";
// Always sha256const SIGNING_SECRET_ALGORITHM = "sha256";
const PORT = 3000;
const app = express();
function isValidSignature(signature, body, timestamp) {  let hmac = crypto.createHmac(SIGNING_SECRET_ALGORITHM, SIGNING_SECRET);  let sig = hmac.update(timestamp + body).digest("base64");
  return (    Buffer.compare(      Buffer.from(signature),      Buffer.from(sig.toString("base64"))    ) === 0  );}
function storeRawBody(req, res, buf) {  if (buf && buf.length) {    req.rawBody = buf.toString("utf8");  }}
// Use middleware to store raw request body depending on request formatapp.use(  express.json({    verify: storeRawBody,  }));app.use(express.urlencoded({ verify: storeRawBody, extended: true }));app.use(express.xml({ verify: storeRawBody }));
app.post("/hook", (req, res) => {  // Fields from the webhook request, this will change every time  const signature = req.headers["x-zendesk-webhook-signature"];  const timestamp = req.headers["x-zendesk-webhook-signature-timestamp"];  const body = req.rawBody;
  console.log(    isValidSignature(signature, body, timestamp)      ? "HMAC signature is valid"      : "HMAC signature is invalid"  );  res.status(200).send("Success");});
app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));

Retrieving a webhook's signing secret key

In the UI

Navigate to the webhook in Admin Center. On the webhook details page, the secret key is hidden by default. It can be revealed by clicking "Reveal secret".

In the API

Use the Show webhook signing secret API to retrieve the secret.

Note: The secret key should be treated like any other credential and should not be committed to code or exposed publicly.

Resetting the secret key

Resetting the secret key will generate a new secret key that will be used for new webhook requests from that time on. You should ensure the new key is updated on your server after changing it, as new requests may not pass verification.

Signing secrets on new webhooks

You may need to test the webhook before it is created. Signing secrets are only generated once a webhook is fully created. This means that to test a webhook during creation, you'll need to use a static signing secret.

The static secret for verifying a test webhook before it is created will always be: dGhpc19zZWNyZXRfaXNfZm9yX3Rlc3Rpbmdfb25seQ==

Note: You'll still need to verify the signature using this secret to validate your testing.

Once the webhook is created, a separate signing secret will be generated for it. Existing webhooks being updated and tested will use the proper signing secret for the webhook, not the static secret.