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

# Security

> Build secure webhook endpoints using webhook signature verification.

<h2 id="webhook-signatures">
  Webhook Signatures
</h2>

To help you secure your webhook handlers so that they only respond to events sent from Trophy and not malicious attackers, Trophy includes a webhook signature with every event.

This signature is sent in the `X-Trophy-Signature` header and is a `base64` encoded hash of the request payload, hashed using a secure webhook secret provided by Trophy.

<h2 id="webhook-secrets">
  Webhook Secrets
</h2>

Each webhook you set up in Trophy has a unique webhook secret which you can access from the [webhooks page](https://app.trophy.so/integration/webhooks) in the Trophy dashboard.

<Warning>
  Make sure you store your webhook secret in a **secure environment variable**
  and do not commit it to source control.
</Warning>

<h2 id="securing-webhook-handlers">
  Securing Webhook Handlers
</h2>

To validate that events your webhook handler receives do actually come from Trophy, you need to create your own hash using your secure [webhook secret](#webhook-secrets) and compare it to the [webhook signature](#webhook-signatures) in the `X-Trophy-Signature` header.

<Frame>
  <video autoPlay muted loop playsInline className="w-full aspect-15/4" src="https://mintcdn.com/trophy/J4Buo3161WwaYk4f/assets/webhooks/copy_secret.mp4?fit=max&auto=format&n=J4Buo3161WwaYk4f&q=85&s=b0c1002e7e17ad28ca80716e769ff2cb" data-path="assets/webhooks/copy_secret.mp4" />
</Frame>

Once you have your webhook secret, you're ready to start validating events. Here's an example in NodeJS:

```js Validating Webhook Events theme={null}
  // Extract X-Trophy-Signature header from the request
  const hmacHeader = request.headers.get("X-Trophy-Signature");

  // Create a hash based on the parsed body
  const hash = crypto
      .createHmac("sha256", process.env.TROPHY_WEBHOOK_SECRET as string)
      .update(await request.text())
      .digest("base64");

  // Compare the created hash with the value of the X-Trophy-Signature header
  if (hash === hmacHeader) {
      console.log("Webhook is originating from Trophy");
      // Request validated, continue processing
  } else {
      console.log("Signature is invalid, rejected");
      // Request is not from Trophy, reject with 4XX status
  }
```

<Warning>
  If your handler detects a request that did not originate from Trophy, it's
  important to reject the request as early as possible with a `4XX` status code.
</Warning>

<h2 id="get-support">
  Get Support
</h2>

Want to get in touch with the Trophy team? Reach out to us via [email](mailto:support@trophy.so). We're here to help!
