Zoho Webhook Signature Validation: Step-by-Step Guide to Secure Your Webhooks
Technology Blogs

Zoho Webhook Signature Validation: Step-by-Step Guide to Secure Your Webhooks

Abhishek D
Associate Software Engineer
Table of Content

Zoho webhook signature validation uses an HMAC-SHA256 signature to sign webhook requests. Validate that signature before you trust or process any webhook data. This guide shows the steps, an example, common mistakes, and a short pseudocode implementation so any developer can follow it in their preferred language.

Why This Matters

Webhook endpoints are public URLs. Without Zoho webhook signature validation, anyone who knows the endpoint can send fake requests. Validating the signature ensures the request:

  • Came from Zoho, and
  • Has not been changed in transit.

The Goal

Calculate the same HMAC-SHA256 signature Zoho sends, then compare it to the X-Zoho-Webhook-Signature header. If they match, the webhook is authentic. This is the core of Zoho webhook signature validation.

Simple Core Steps

🔹Extract Data

  • Query string from the URL (everything after ?)
  • Raw JSON request body (exact bytes or characters as received)
  • Signature header X-Zoho-Webhook-Signature

🔹Build the Exact String to Hash

  • Remove trailing & from query string, if present.
  • Parse query parameters into key/value pairs.
  • Sort parameters alphabetically by key.
  • Concatenate each key then value with no = or & (i.e., keyvalue).
  • After all params, append the full raw JSON body (no reformatting).

🔹Generate the HMAC

  • Use HMAC-SHA256 with your webhook secret as the key.
  • Hash the constructed string.
  • Convert the result to a lowercase hexadecimal (or the format your language/library yields).

🔹Compare

  • Compare your calculated signature with the header value.
  • Use case-insensitive comparison (or normalize both to lowercase).

Quick Example

Incoming request:

URL: /api/invoices/webhook?invoice_id=2865984000000050002&invoice_status=Sent&
Header: X-Zoho-Webhook-Signature: 724b194fd45e…
Body: {“invoice_id”:”2865984000000050002″,”invoice_status”:”Sent”}

String to hash (after sorting & concatenating):

invoice_id2865984000000050002invoice_statusSent{“invoice_id”:”2865984000000050002″,”invoice_status”:”Sent”}

Hash that string with HMAC-SHA256 using the secret, then compare. This comparison step is the heart of Zoho webhook signature validation.

Pseudocode (Language-agnostic)

function validateWebhookSignature(queryString, rawBody, receivedSignature, secretToken):

// 1. Clean and parse query string
if queryString endsWith "&":
queryString = queryString without last char

params = parseQueryString(queryString) // returns map/dict of key->value
sortedKeys = sortAlphabetically(params.keys)

// 2. Build stringToHash
stringToHash = ""
for key in sortedKeys:
stringToHash += key + params[key]
stringToHash += rawBody // rawBody must be exact raw JSON

// 3. Compute HMAC-SHA256
calculated = HMAC_SHA256(secretToken, stringToHash)
calculatedHex = toLowerHex(calculated)

// 4. Compare (case-insensitive)
return calculatedHex == toLower(receivedSignature)

Java (Spring Boot) Example for Zoho Webhook Signature Validation

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import org.apache.commons.codec.binary.Hex;

public class ZohoWebhookValidator {

public static boolean validateWebhookSignature(
String queryString, String rawBody,
String receivedSignature, String secretToken) {

try {
// 1. Clean query string
if (queryString.endsWith("&")) {
queryString = queryString.substring(0, queryString.length() - 1);
}

// 2. Parse & sort query params
String[] pairs = queryString.split("&");
java.util.Map<String, String> params = new java.util.HashMap<>();
for (String pair : pairs) {
String[] kv = pair.split("=", 2);
params.put(kv[0], kv.length > 1 ? kv[1] : "");
}

java.util.List<String> sortedKeys = new java.util.ArrayList<>(params.keySet());
java.util.Collections.sort(sortedKeys);

// 3. Build stringToHash
StringBuilder sb = new StringBuilder();
for (String key : sortedKeys) {
sb.append(key).append(params.get(key));
}
sb.append(rawBody);

String stringToHash = sb.toString();

// 4. Compute HMAC-SHA256
SecretKeySpec secretKey = new SecretKeySpec(secretToken.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
byte[] hash = mac.doFinal(stringToHash.getBytes(StandardCharsets.UTF_8));

String calculatedSignature = Hex.encodeHexString(hash).toLowerCase();

// 5. Compare (case-insensitive)
return calculatedSignature.equalsIgnoreCase(receivedSignature);

} catch (Exception e) {
throw new RuntimeException("Error validating Zoho webhook signature", e);
}
}
}

Ready to Level Up Your Security?

Advanced Tips & Implementation Notes

  • Capture raw body: Many frameworks parse JSON and lose exact formatting (whitespace, escaped sequences). Use a middleware/filter to capture the raw request body bytes before parsing.
  • Unicode: Keep escapes (e.g., \u20ac) the same as received; do not let string normalization change them.
  • Trailing ampersand: Remove trailing & before parsing.
  • Sorting: Always sort by parameter key (lexicographic). Use a stable sorted map or explicit sort.
  • Header format: Trim whitespace from the header before comparing.

These best practices ensure accurate Zoho webhook signature validation across environments.

Common Mistakes

  • Using key=value instead of keyvalue.
  • Not sorting parameters.
  • Using the parsed/pretty JSON body rather than the raw body.
  • Secret token mismatch due to extra spaces or wrong env var.
  • Logging the secret or full raw webhook in production.

Checklist

  • Secret token stored securely (env var or secret manager).
  • Raw request body captured before parsing.
  • Trailing & removed from query string.
  • Query parameters sorted alphabetically.
  • Concatenation is keyvalue, then raw body appended.
  • HMAC-SHA256 used with secret token key.
  • Signature comparison is case-insensitive.
  • Special characters and Unicode handled by using the raw bytes.

Following this checklist ensures every Zoho webhook signature validation runs correctly and securely.

coma

Conclusion

Validating Zoho webhook signatures might look tricky at first, but the process is straightforward once you break it into steps: extract → build string → HMAC → compare. With Zoho webhook signature validation, you ensure that only authentic requests from Zoho are processed by your system.

Think of it as the lock on your webhook endpoint—without it, anyone could walk in. With Zoho webhook signature validation, you know every request is genuine.

Abhishek D

Abhishek D

Associate Software Engineer

Abhishek is a full-stack developer proficient in ReactJs and Java having 2+ years of experience. He is passionate about creating user-friendly and engaging web experiences. He can build and maintain web applications from the front end to the back end. He has a problem-solving mentality and is constantly eager to learn new technologies.

Share This Blog

Read More Similar Blogs

Let’s Transform
Healthcare,
Together.

Partner with us to design, build, and scale digital solutions that drive better outcomes.

Location

5900 Balcones Dr, Ste 100-7286, Austin, TX 78731, United States

Contact form