> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clarionhealth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Verifying Webhook Signatures

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:

| Header                | Description                                               |
| --------------------- | --------------------------------------------------------- |
| `X-Clarion-Signature` | The hex-encoded ECDSA signature                           |
| `X-Clarion-Timestamp` | Unix timestamp (milliseconds) when the request was signed |
| `X-Clarion-Secret`    | Your 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

```javascript theme={null}
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

<Note>
  If your endpoint does not return a `200` status code, Clarion may retry the webhook delivery.
</Note>
