Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.khaime.com/llms.txt

Use this file to discover all available pages before exploring further.

Webhook Signature Verification

Every webhook request includes an X-Khaime-Signature header containing an HMAC-SHA256 hash of the raw request body, signed with your webhook secret. Always verify signatures before processing webhooks to prevent spoofed events.

How It Works

  1. Khaime computes HMAC-SHA256(webhook_secret, raw_request_body)
  2. Sends the hex digest in X-Khaime-Signature
  3. You recompute the same hash using the raw request body and compare
Use the raw request body (not a re-serialized version) for signature verification. Re-serializing JSON can change key ordering or whitespace, causing verification to fail.

Implementation

const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In Express, use express.raw() to get the raw body:
app.post('/webhooks/khaime', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString();
  const signature = req.headers['x-khaime-signature'];

  if (!verifyWebhook(rawBody, signature, process.env.KHAIME_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(rawBody);
  // Process event...
  res.status(200).send('OK');
});
Use constant-time comparison (timingSafeEqual, compare_digest, hash_equals) to prevent timing attacks. Never use === or == for signature comparison.