Skip to main content
Clarion provides two methods for verifying webhook authenticity:
  1. ECDSA Signature - All requests are signed using ECDSA with SHA-256
  2. Custom Secret - Optionally configure your own secret in the dashboard

Signature Headers

Each webhook request includes the following headers:
HeaderDescription
X-Clarion-SignatureThe hex-encoded ECDSA signature
X-Clarion-TimestampUnix timestamp (milliseconds) when the request was signed
X-Clarion-SecretYour custom secret (if configured in the dashboard)

Custom Secret Verification

You can configure a custom secret in the Clarion dashboard under Developer Actions. When set, this secret is sent with every webhook request in the X-Clarion-Secret header. Simply compare the header value against your stored secret to verify the request.

ECDSA Signature Verification

For stronger security, you can verify the ECDSA signature. Use the following public key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUTo6buTe5YfARuqL9LEHbfxij5/Y
2eRkCAkNd8zmHfAktsWJcrGQ5jZzG0Rohni7b+4FGqvtZn5JZT64hRRyRg==
-----END PUBLIC KEY-----

Verification Steps

  1. Extract the X-Clarion-Signature and X-Clarion-Timestamp headers from the request
  2. Get the raw request body as a string
  3. Construct the signed payload by concatenating: {timestamp}.{body}
  4. Verify the signature using ECDSA with SHA-256 and the public key above
  5. Optionally, check that the timestamp is within an acceptable time window (e.g., 5 minutes) to prevent replay attacks

Node.js Verification Example

const crypto = require('crypto');

const CLARION_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUTo6buTe5YfARuqL9LEHbfxij5/Y
2eRkCAkNd8zmHfAktsWJcrGQ5jZzG0Rohni7b+4FGqvtZn5JZT64hRRyRg==
-----END PUBLIC KEY-----`;

function verifyClarionWebhook(body, signature, timestamp) {
  // Verify timestamp is recent (within 5 minutes)
  const timestampMs = parseInt(timestamp, 10);
  const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
  if (timestampMs < fiveMinutesAgo) {
    return false;
  }

  // Verify signature
  const verifier = crypto.createVerify('SHA256');
  verifier.update(`${timestampMs}.${body}`);
  return verifier.verify(CLARION_PUBLIC_KEY, signature, 'hex');
}

// Example Express handler
app.post('/my-webhook-endpoint', (req, res) => {
  const signature = req.headers['X-Clarion-Signature'];
  const timestamp = req.headers['X-Clarion-Timestamp'];
  const rawBody = JSON.stringify(req.body);

  if (!verifyClarionWebhook(rawBody, signature, timestamp)) {
    return res.status(401).send('Invalid signature');
  }

  // Handle the webhook payload
  console.log('Received valid webhook:', req.body);
  res.status(200).send('OK');
});

Handling the Webhook

Your webhook endpoint should:
  1. Accept POST requests with a JSON body
  2. Verify the signature using the method above
  3. Return a 200 status code to acknowledge receipt
  4. Process the payload asynchronously if needed to avoid timeouts
If your endpoint does not return a 200 status code, Clarion may retry the webhook delivery.