# Webhook signature verification

**Summary:** Verify that a webhook request comes from Subrite by computing an HMAC SHA256 of the timestamp and payload with your webhook secret. Includes a NestJS example.

- Space: [Developers](https://www.subrite.no/developers)
- Source: https://www.subrite.no/developers/webhook-signature-verification
- Updated: 2026-09-23
- Markdown index: https://www.subrite.no/developers/llms.txt

Upon receiving the request at your endpoint, it is recommended to verify the signature to ensure the request's authenticity and integrity. This can be done by comparing the `x-subrite-webhook-signature` header with a computed signature based on your [webhook secret](https://www.subrite.no/developers/create-webhook?md=true) and the request payload. The `x-subrite-webhook-signature-timestamp` header can be used to prevent replay attacks by ensuring the request is within an acceptable time frame.

<a id="verification-steps"></a>
## Verification steps

Here is a basic outline of the steps for signature verification:

1. **Retrieve the signature and timestamp from the headers:**
   - `x-subrite-webhook-signature`
   - `x-subrite-webhook-signature-timestamp`
2. **Create a signature string:** Combine the timestamp and the request payload.
3. **Compute the HMAC:** Use your webhook secret and the combined string to compute an HMAC SHA256 hash.
4. **Compare signatures:** Compare the computed hash with the received signature.

By implementing these steps, you can ensure that the request is genuinely from Subrite and has not been tampered with during transmission.

<a id="nestjs-example"></a>
## NestJS example

Below is a sample implementation in TypeScript for verifying the signature of incoming webhook requests in a NestJS application. This example demonstrates how to extract the signature and timestamp from the request headers, compute the HMAC signature, and compare it to the received signature.

```typescript
import { Controller, Post, Body, Headers, HttpException, HttpStatus } from '@nestjs/common';
import * as crypto from 'crypto';

@Controller('webhook')
export class WebhookController {
  @Post('test')
  testWebhook(@Body() body: unknown, @Headers() headers: Record<string, string>) {
    const signature = headers['x-subrite-webhook-signature'] as string;
    const timestamp = headers['x-subrite-webhook-signature-timestamp'] as string;

    if (!this.verifySignature(signature, timestamp, JSON.stringify(body))) {
      throw new HttpException('Invalid signature', HttpStatus.UNAUTHORIZED);
    }

    // Implement your logic
  }

  verifySignature(signature: string, timestamp: string, body: string = '') {
    const secret = 'your-webhook-secret';
    const algorithm = 'sha256';

    const computedSignature = this.computeSignature(secret, algorithm, timestamp, body);
    return computedSignature === signature;
  }

  computeSignature(
    secret: string,
    algorithm: string,
    timestamp: string,
    body: string = '',
  ): string {
    const data = body || '';
    const hmac = crypto.createHmac(algorithm, secret);
    const signature = hmac.update(timestamp + data).digest('base64');
    return signature;
  }
}
```
