Documentation

Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry

Custom HMAC SHA256 Webhooks

HMAC SHA256 is the industry-standard method for securing webhooks. Use this for custom integrations or APIs that don't match Stripe/GitHub/Shopify formats.

How HMAC SHA256 Works

HMAC (Hash-based Message Authentication Code) combines a secret key with your webhook payload to create a unique signature:

signature = HMAC-SHA256(secret_key, request_body)
  1. Sender: Computes HMAC of payload using shared secret
  2. Sender: Includes signature in HTTP header
  3. Receiver: Recomputes HMAC using same secret
  4. Receiver: Compares signatures to verify authenticity

Supported Header Names

Hookmetry automatically checks these common header names:

X-SignatureX-Webhook-SignatureX-Hub-SignatureX-HMAC-Signature

Creating HMAC Signatures

Node.js

const crypto = require('crypto');

function generateHMAC(payload, secret) {
  const hmac = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return hmac;
}

// Usage
const payload = { event: 'user.created', userId: 123 };
const secret = 'your_webhook_secret';
const signature = generateHMAC(payload, secret);

// Send webhook
fetch('https://hookmetry.com/webhook/ep_abc123', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Signature': signature
  },
  body: JSON.stringify(payload)
});

Python

import hmac
import hashlib
import json
import requests

def generate_hmac(payload, secret):
    message = json.dumps(payload).encode('utf-8')
    secret_bytes = secret.encode('utf-8')
    signature = hmac.new(secret_bytes, message, hashlib.sha256).hexdigest()
    return signature

# Usage
payload = {'event': 'user.created', 'userId': 123}
secret = 'your_webhook_secret'
signature = generate_hmac(payload, secret)

# Send webhook
requests.post(
    'https://hookmetry.com/webhook/ep_abc123',
    json=payload,
    headers={'X-Signature': signature}
)

PHP

<?php
function generateHMAC($payload, $secret) {
    $json = json_encode($payload);
    $signature = hash_hmac('sha256', $json, $secret);
    return $signature;
}

// Usage
$payload = ['event' => 'user.created', 'userId' => 123];
$secret = 'your_webhook_secret';
$signature = generateHMAC($payload, $secret);

// Send webhook
$ch = curl_init('https://hookmetry.com/webhook/ep_abc123');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-Signature: ' . $signature
]);
curl_exec($ch);
?>

Verifying HMAC Signatures (Receiver Side)

const crypto = require('crypto');

function verifyHMAC(payload, receivedSignature, secret) {
  // Compute expected signature
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  // Use timing-safe comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(receivedSignature),
    Buffer.from(expectedSignature)
  );
}

// Express.js middleware example
app.post('/webhook', express.json(), (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  if (!verifyHMAC(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Process webhook
  console.log('Valid webhook received:', req.body);
  res.status(200).json({ received: true });
});

Security Best Practices:

  • • Use long, randomly-generated secrets (32+ characters)
  • • Never hardcode secrets in your code - use environment variables
  • • Use timing-safe comparison functions to prevent timing attacks
  • • Rotate secrets periodically (every 90 days recommended)
  • • Always use HTTPS to prevent man-in-the-middle attacks

Common HMAC Pitfalls

Mismatched Payload Serialization

The sender and receiver must serialize the payload identically. Watch out for:

  • • Different JSON key ordering
  • • Extra whitespace or newlines
  • • Different encoding (UTF-8 vs ASCII)

Using Raw Body vs Parsed JSON

If you parse JSON before verification, you may alter the payload. Always verify against the raw body.

Case Sensitivity Issues

HMAC signatures are case-sensitive. Don't convert to uppercase/lowercase.

Testing HMAC Webhooks:

Create a Custom HMAC endpoint in Hookmetry, set your secret, then use the code examples above to send test webhooks. Check the webhook logs to see validation results and debug signature mismatches.