# Verifying Requests

Every webhook request includes an `aktify-signature` header. Use this to verify that the request originated from Aktify and hasn't been tampered with.

## Signature Format

The signature format depends on the webhook version:

| Version | Format | Description |
| :--- | :--- | :--- |
| v1 (Legacy) | `t=<unix_timestamp_ms>,v1=<signature>` | HMAC computed over the request body only |
| v2 | `t=<unix_timestamp_ms>,v2=<signature>` | HMAC computed over the timestamp and request body |

| Component | Description |
| :--- | :--- |
| `t` | Unix timestamp (milliseconds) when the request was sent |
| `v1` | HMAC SHA-256 hex digest of `JSON.stringify(body)` |
| `v2` | HMAC SHA-256 hex digest of `${timestamp}.${JSON.stringify(body)}` |

The version component (`v1` or `v2`) tells you which verification method to use. Both versions use your **client secret** as the HMAC key.

## v1 Verification (Legacy Events)

v1 signatures sign only the request body. The timestamp is included in the header but is **not** part of the signed payload, so you should independently check for stale timestamps to guard against replay attacks.

```javascript
const crypto = require('crypto');

function verifyV1Webhook(requestBody, signatureHeader, clientSecret) {
  const [tPart, v1Part] = signatureHeader.split(',');
  const timestamp = tPart.split('=')[1];
  const signature = v1Part.split('=')[1];

  const expectedSignature = crypto
    .createHmac('sha256', clientSecret)
    .update(JSON.stringify(requestBody))
    .digest('hex');

  if (signature !== expectedSignature) {
    throw new Error('Invalid signature - request may not be from Aktify');
  }

  // Recommended: reject requests older than 5 minutes to prevent replay attacks
  const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
  if (parseInt(timestamp) < fiveMinutesAgo) {
    throw new Error('Request timestamp too old');
  }

  return true;
}
```

## v2 Verification

v2 signatures include the timestamp in the HMAC input, cryptographically binding the timestamp to the payload. This means any modification to the timestamp invalidates the signature, providing built-in replay protection.

```javascript
const crypto = require('crypto');

function verifyV2Webhook(requestBody, signatureHeader, clientSecret) {
  const [tPart, v2Part] = signatureHeader.split(',');
  const timestamp = tPart.split('=')[1];
  const signature = v2Part.split('=').slice(1).join('=');

  const expectedSignature = crypto
    .createHmac('sha256', clientSecret)
    .update(`${timestamp}.${JSON.stringify(requestBody)}`)
    .digest('hex');

  if (signature !== expectedSignature) {
    throw new Error('Invalid signature - request may not be from Aktify');
  }

  // Reject requests older than 5 minutes
  const fiveMinutesAgo = Date.now() - (5 * 60 * 1000);
  if (parseInt(timestamp) < fiveMinutesAgo) {
    throw new Error('Request timestamp too old');
  }

  return true;
}
```
