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

# Verify webhook requests

> Learn how to verify webhook requests to ensure they're coming from Dock.

With signature verification, you can determine if the webhook came from Dock, and has not been tampered with in transit.

All webhooks are delivered with a `X-Dock-Signature` and `X-Timestamp` header. Dock generates this header using a secret key that only you and Dock know.

An example header looks like this:

```
X-Dock-Signature: c9ed6a2abf93f59d761eea69908d8de00f4437b5b6d7cd8b9bf5719cbe61bf46
X-Timestamp: 1716998400
```

## Finding your webhook's signing secret

You can find your webhook's signing secret in the **Webhook** page by clicking on the **View Secret Key** button:

<Frame>
  <img src="https://mintcdn.com/docklabsinc/sOq5EJF8JHZxp7ZE/images/webhook-view-secret.png?fit=max&auto=format&n=sOq5EJF8JHZxp7ZE&q=85&s=c2fd90b3d7e81cc34a392cf3ef25b1cc" alt="Webhook signing secret" width="3308" height="1406" data-path="images/webhook-view-secret.png" />
</Frame>

Make sure to keep this secret safe by only storing it in a secure environment variable (e.g. `DOCK_WEBHOOK_SECRET`). Do not commit it to git or add it in any client-side code.

## Verifying a webhook request

To verify, you can use the secret key to generate your own signature for webhook. If both signatures match then you can be sure that a received event came from Dock.

The steps required are:

1. Get the raw body of the request.
2. Extract the signature from the `X-Dock-Signature` header.
3. Calculate the HMAC of the `Request Method + Target Url + Raw Body` using the `SHA-256` hash function and the secret.
4. Compare the calculated `HMAC` with the one sent in the `X-Dock-Signature` header. If they match, the webhook is verified.

Here's an example of how you can verify a webhook request in different languages:

<CodeGroup>
  ```javascript Node.js theme={null}
  export const POST = async (req: Request,res: Response) => {
    const webhookSignature = req.headers.get("X-Dock-Signature");
    if (!webhookSignature) {
      return res.status(401).send("No signature provided.");
    }

    // Copy this from the webhook details page
    const secret = process.env.DOCK_WEBHOOK_SECRET;
    if (!secret) {
      return res.status(401).send("No secret provided.");
    }

    // Make sure to get the body from the request
    const url = `https://${req.header('host')}${req.path}`;
    const body = JSON.stringify(req.body);
    const payload = `${req.method}\n${url}\n${body}`;

    const computedSignature = crypto
      .createHmac("sha256", secret)
      .update(payload)
      .digest("hex");

    if (crypto.timingSafeEqual(
        Buffer.from(webhookSignature, 'hex'),
        Buffer.from(computedSignature, 'hex'),
      )) {
      return res.status(400).send("Invalid signature");
    }

    // Handle the webhook event
    // ...
  };
  ```

  ```python Python theme={null}
  import hmac
  import hashlib
  import os
  from flask import Flask, request, Response

  app = Flask(__name__)

  @app.route('/webhook', methods=['POST'])
  def webhook_handler():
      webhook_signature = request.headers.get("X-Dock-Signature")
      if not webhook_signature:
          return Response("No signature provided.", status=401)

      secret = os.getenv("DOCK_WEBHOOK_SECRET")
      if not secret:
          return Response("No secret provided.", status=401)

      body = request.get_data(as_text=True)
      url = f"{request.host}{request.path}"
      payload = f"{request.method}\n{url}\n{body}"

      computed_signature = hmac.new(
          secret.encode('utf-8'),
          payload.encode('utf-8'),
          hashlib.sha256
      ).hexdigest()

      if not hmac.compare_digest(webhook_signature, computed_signature):
          return Response("Invalid signature.", status=400)

      ## Handle the webhook event
      ## ...


  ```

  ```go Golang theme={null}
  package main

  import (
  	"bytes"
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/hex"
  	"io"
  	"net/http"
  	"os"
  )

  func webhookHandler(w http.ResponseWriter, r *http.Request) {
  	webhookSignature := r.Header.Get("X-Dock-Signature")
  	if webhookSignature == "" {
  		http.Error(w, "No signature provided.", http.StatusUnauthorized)
  		return
  	}

  	secret := os.Getenv("DOCK_WEBHOOK_SECRET")
  	if secret == "" {
  		http.Error(w, "No secret provided.", http.StatusUnauthorized)
  		return
  	}

  	bodyBytes, err := io.ReadAll(r.Body)
  	if err != nil {
  		http.Error(w, "Unable to read request body.", http.StatusInternalServerError)
  		return
  	}

  	// Reconstruct the payload
  	url := r.Host + r.URL.Path
  	payload := r.Method + "\n" + url + "\n" + string(bodyBytes)

  	h := hmac.New(sha256.New, []byte(secret))
  	h.Write([]byte(payload))
  	computedSignature := hex.EncodeToString(h.Sum(nil))

  	if !hmac.Equal([]byte(webhookSignature), []byte(computedSignature)) {
  		http.Error(w, "Invalid signature.", http.StatusBadRequest)
  		return
  	}

  	// Handle the webhook event
      //...
  }
  ```
</CodeGroup>

## Why is signature verification important?

Signature verification is a crucial security measure that protects against request forgery and data tampering. Without verification, malicious actors could send fake webhook events to your endpoint, potentially triggering unauthorized actions.

The HMAC-SHA256 signature verification process ensures that only Dock can generate valid webhook requests and that payloads haven't been modified in transit. This provides both authentication (confirming the sender is Dock) and integrity (ensuring the message hasn't been tampered with).
