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

# Webhook authentication

> Verify OpenFX webhook events with the x-openfx-signature header using HMAC-SHA256.

We recommend verifying all webhook events using the `x-openfx-signature` header.

## Signature verification

To verify the signature, generate an `HMAC-SHA256` hash of the request payload using the webhook signing key and compare it with the value of the `x-openfx-signature` header.

<CodeGroup>
  ```javascript JavaScript theme={null}
  function isValidSignature(request) {
    const signingKey = "Signing-key-provided-by-OpenFX"
    const headers = request.headers
    const signature = headers["x-openfx-signature"]

    const body = request.body
    const hmac = crypto.createHmac("sha256", signingKey) // Create a HMAC SHA256 hash using the key
    hmac.update(body) // Pass body as is, in json string format.
    const computedHash = hmac.digest("base64")

    return signature === computedHash // If signature equals your computed hash, return true
  }
  ```

  ```python Python theme={null}
  import hmac
  import hashlib
  import base64

  def is_valid_signature(request):
      signing_key = "Signing-key-provided-by-OpenFX"
      signature = request["headers"].get("x-openfx-signature")
      body = request["body"].encode("utf-8")

      hmac_obj = hmac.new(signing_key.encode("utf-8"), body, hashlib.sha256)
      computed_hash = base64.b64encode(hmac_obj.digest()).decode()

      return signature == computed_hash
  ```

  ```java Java theme={null}
  import javax.crypto.Mac;
  import javax.crypto.spec.SecretKeySpec;
  import java.util.Base64;

  public class SignatureValidator {
      private static final String SIGNING_KEY = "Signing-key-provided-by-OpenFX";

      public static boolean isValidSignature(Request request) throws Exception {
          String signature = request.getHeaders().get("x-openfx-signature");
          String body = request.getBody();

          Mac mac = Mac.getInstance("HmacSHA256");
          SecretKeySpec secretKeySpec = new SecretKeySpec(SIGNING_KEY.getBytes(), "HmacSHA256");
          mac.init(secretKeySpec);
          byte[] hmacBytes = mac.doFinal(body.getBytes());

          String computedHash = Base64.getEncoder().encodeToString(hmacBytes);
          return signature.equals(computedHash);
      }
  }
  ```

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

  import (
  	"crypto/hmac"
  	"crypto/sha256"
  	"encoding/base64"
  	"fmt"
  )

  func isValidSignature(request map[string]interface{}) bool {
  	signingKey := "Signing-key-provided-by-OpenFX"
  	headers := request["headers"].(map[string]string)
  	signature := headers["x-openfx-signature"]
  	body := request["body"].(string)

  	h := hmac.New(sha256.New, []byte(signingKey))
  	h.Write([]byte(body))
  	computedHash := base64.StdEncoding.EncodeToString(h.Sum(nil))

  	return signature == computedHash
  }

  func main() {
  	request := map[string]interface{}{
  		"headers": map[string]string{"x-openfx-signature": "expected_signature"},
  		"body":    `{"example": "data"}`,
  	}
  	fmt.Println(isValidSignature(request))
  }
  ```

  ```php PHP theme={null}
  <?php
  function isValidSignature($request) {
      $signingKey = "Signing-key-provided-by-OpenFX";
      $headers = $request['headers'];
      $signature = $headers['x-openfx-signature'];
      $body = $request['body'];

      $computedHash = base64_encode(hash_hmac('sha256', $body, $signingKey, true));

      return hash_equals($signature, $computedHash);
  }
  ?>
  ```
</CodeGroup>
