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

# Webhook validator

> Check a Spotflow webhook signature in your browser, to debug your verification code.

> [!WARNING]
>
> Everything runs in your browser and nothing is sent anywhere. Even so, use your **test-mode** webhook secret here, never your live one.

## Gather what you need

| Input | Where to find it |
| - | - |
| Webhook secret | Your Spotflow dashboard, where you set your webhook URL (**Settings → API Keys**). Test and live mode are configured separately |
| `webhook-id` | Request header on the webhook. Also use it to ignore duplicate deliveries |
| `webhook-timestamp` | Request header, in Unix seconds |
| `x-spotflow-signature` | Request header on the webhook. It looks like `v1,` followed by a base64 string |
| Raw request body | Log it on your server **before** parsing it as JSON |

## Validate

Webhook secret

webhook-id

webhook-timestamp

x-spotflow-signature

Raw request body

Validate signature

## What it checks

Spotflow webhooks follow the [Standard Webhooks](https://www.standardwebhooks.com/) specification. The validator does what the official Standard Webhooks libraries do:

1. Takes your secret, removes any `whsec_` prefix, and base64-decodes it.
2. Computes an HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{raw body}`.
3. Base64-encodes the result and compares it with each `v1,` signature in the header.

It also tells you whether the timestamp is within the 5-minute window the libraries enforce.

## Verify in your code

Use the official library rather than writing your own comparison:

```javascript theme={null}
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.SPOTFLOW_WEBHOOK_KEY);

// rawBody must be the exact text you received, not re-serialised JSON
const payload = webhook.verify(rawBody, {
  "webhook-id": req.headers["webhook-id"],
  "webhook-timestamp": req.headers["webhook-timestamp"],
  // Spotflow sends the signature as x-spotflow-signature
  "webhook-signature": req.headers["x-spotflow-signature"],
});
```

## If the signature doesn't match

* **The body changed.** Parsing and re-serialising JSON changes whitespace and key order. Verify the raw bytes you received.
* **Wrong secret.** Test and live mode have different webhook secrets.
* **Extra whitespace.** Copy header values exactly, with no trailing spaces or line breaks.
* **Wrong separator.** The three parts are joined with a single `.` and nothing else.
* **Wrong encoding.** The signature is base64, after the `v1,` prefix.

See [Webhooks](https://docs.spotflow.one/developer-resources/webhooks/overview.md) for setup and delivery behaviour.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.