The token is automatically generated by Zoho Webinar
Click Copy to copy the token and add it to your receiving application
Click Regenerate to create a new token if the current one is compromised. Regenerating invalidates the previous token immediately.
The Body contains the data payload that Zoho Webinar sends to the receiving application with each Webhook request. You can send the payload in one of two formats:
JSON format is used when the receiving application expects structured, machine-readable data. The payload is written as a JSON object with key-value pairs representing the event data you want to send.
Form Data format is used when the receiving application expects the payload submitted as a URL-encoded string. Each value is URL-encoded and pairs are joined with &.
Body Sent
{"webinarId":"12345","event":"registration","email":"user@example.com"}
Header:
X-WEBINAR-SIGNATURE: a1b2c3d4e5f6...
Content-Type: application/json
Node.js
const crypto = require("crypto");
/**
* Verifies an X-WEBINAR-SIGNATURE header. Works for both JSON and
* form-data webhooks — just pass the raw request body.
*
* @param {string} rawBody Raw HTTP request body as received (JSON text or
* url-encoded form string); never parsed/re-serialized.
* @param {string} signatureHeader Value of the X-WEBINAR-SIGNATURE header.
* @param {string} secret The org's webhook signature key (token).
* @returns {boolean} True if the signature is valid.
*/
function verifySignature(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) {
return false;
}
const body = rawBody || "";
const expected = crypto
.createHmac("sha256", secret)
.update(body, "utf8")
.digest("hex"); // lowercase hex
const expectedBuf = Buffer.from(expected, "utf8");
const receivedBuf = Buffer.from(signatureHeader.trim().toLowerCase(), "utf8");
// Lengths must match for timingSafeEqual
if (expectedBuf.length !== receivedBuf.length) {
return false;
}
return crypto.timingSafeEqual(expectedBuf, receivedBuf);
}
// Example usage
const secret = "your-webhook-signature-token";
// JSON webhook: rawBody is the JSON text as received
const jsonBody = '{"name":"Ann","id":42}';
// Form-data webhook: rawBody is the url-encoded form string as received
const formBody = "name=Ann¬e=hi+there";
const signatureFromHeader = "..."; // X-WEBINAR-SIGNATURE value
const isValid = verifySignature(jsonBody, signatureFromHeader, secret);
console.log(isValid ? "Signature is valid!" : "Signature is invalid!");
Python
import hmac
import hashlib
def verify_signature(raw_body: str, signature_header: str, secret: str) -> bool:
"""
Verifies an X-WEBINAR-SIGNATURE header. Works for both JSON and
form-data webhooks -- just pass the raw request body.
:param raw_body: Raw HTTP request body as received (JSON text or
url-encoded form string); never parsed/re-serialized.
:param signature_header: Value of the X-WEBINAR-SIGNATURE header.
:param secret: The org's webhook signature key (token).
:return: True if the signature is valid.
"""
if not signature_header or not secret:
return False
body = raw_body or ""
expected = hmac.new(
secret.encode("utf-8"),
body.encode("utf-8"),
hashlib.sha256,
).hexdigest() # lowercase hex
# Constant-time comparison (header normalized to lowercase)
return hmac.compare_digest(expected, signature_header.strip().lower())
# Example usage
if __name__ == "__main__":
secret = "your-webhook-signature-token"
# JSON webhook: raw_body is the JSON text as received
json_body = '{"name":"Ann","id":42}'
# Form-data webhook: raw_body is the url-encoded form string as received
form_body = "name=Ann¬e=hi+there"
signature_from_header = "..." # X-WEBINAR-SIGNATURE value
is_valid = verify_signature(json_body, signature_from_header, secret)
print("Signature is valid!" if is_valid else "Signature is invalid!")
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class WebinarSignatureValidator {
/**
* Verifies an X-WEBINAR-SIGNATURE header. Works for both JSON and
* form-data webhooks — just pass the raw request body.
*
* @param rawBody the raw HTTP request body as received (JSON text or
* url-encoded form string); never parsed/re-serialized
* @param signatureHeader value of the X-WEBINAR-SIGNATURE header
* @param secret the org's webhook signature key (token)
* @return true if the signature is valid
*/
public static boolean verifySignature(String rawBody, String signatureHeader, String secret) {
if (signatureHeader == null || signatureHeader.isEmpty() || secret == null) {
return false;
}
try {
String body = rawBody != null ? rawBody : "";
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] raw = mac.doFinal(body.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(raw.length * 2);
for (byte b : raw) {
hex.append(Character.forDigit((b >>> 4) & 0xF, 16));
hex.append(Character.forDigit(b & 0xF, 16));
}
String expected = hex.toString();
// Constant-time comparison (header normalized to lowercase)
return MessageDigest.isEqual(
expected.getBytes(StandardCharsets.UTF_8),
signatureHeader.trim().toLowerCase().getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
return false;
}
}
// Example usage
public static void main(String[] args) {
String secret = "your-webhook-signature-token";
// JSON webhook: rawBody is the JSON text as received
String jsonBody = "{\"name\":\"Ann\",\"id\":42}";
// Form-data webhook: rawBody is the url-encoded form string as received
String formBody = "name=Ann¬e=hi+there";
String signatureFromHeader = "..."; // X-WEBINAR-SIGNATURE value
boolean valid = verifySignature(jsonBody, signatureFromHeader, secret);
System.out.println(valid ? "Signature is valid!" : "Signature is invalid!");
}
}