# Validating webhook payloads

> Check the X-Mothership-Signature header with your webhook configuration's secret key.

The [Create a webhook configuration](https://developers.mothership.com/reference/createwebhookconfig) and [Retrieve a webhook configuration](https://developers.mothership.com/reference/fetchwebhookconfig) responses each contain a `secretKey` property that can be used to validate the signature of incoming Mothership webhook events.

Secret keys are unique to webhook configs.

```json Create Webhook 201 Response
{
  "data": {
    "id": "48asjgaoi33nkl4t3lae",
    "createdAt": "2019-03-20T16:17:13.424Z",
    "status": "active",
    "subscribedEvents": ["shipment.purchased", "billOfLading.created", "proofOfDelivery.created"],
    "webhookUrl": "https://www.yourserver.io/webhook/mothership",
    "secretKey": "de3ADDAek3245DLAdANe91Dn3asklAMSsna03aGKkl23agsJea"
  }
}
```

The `X-Mothership-Signature` header is the hex-encoded HMAC-SHA256 of the raw request body, keyed with the secret key. To validate a payload, compute the same HMAC over the body exactly as you received it and compare it to the header.

> [!WARNING] Sign the raw body
> Compute the signature before parsing the JSON. Parsing and re-serializing the body can change spacing or key order, and the signature will no longer match.

Let's validate a webhook payload. Both examples read the secret key from the `MOTHERSHIP_WEBHOOK_SECRET` environment variable and use a constant-time comparison.

```javascript Node.js
import crypto from 'node:crypto'
import express from 'express'

const app = express()
// Check the signature against the raw body, before any JSON parsing.
const rawBody = express.raw({ type: 'application/json' })

app.post('/webhooks/mothership', rawBody, (req, res) => {
  const expected = crypto
    .createHmac('sha256', process.env.MOTHERSHIP_WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex')
  const received = req.get('X-Mothership-Signature') ?? ''

  if (
    !/^[a-f0-9]{64}$/.test(received) ||
    !crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))
  ) {
    return res.sendStatus(401)
  }

  const event = JSON.parse(req.body.toString('utf8'))
  // event.eventType, event.data, event.sentAt, event.deliveryAttempt, event.pendingRetries
  res.sendStatus(200)
})
```
```python Python
import hashlib
import hmac
import os
import re

from flask import Flask, abort, request

app = Flask(__name__)
SECRET = os.environ["MOTHERSHIP_WEBHOOK_SECRET"].encode()

@app.post("/webhooks/mothership")
def mothership_webhook():
    # Check the signature against the raw body, before any JSON parsing.
    expected = hmac.new(SECRET, request.get_data(), hashlib.sha256).hexdigest()
    received = request.headers.get("X-Mothership-Signature", "")
    if not re.fullmatch(r"[a-f0-9]{64}", received) or not hmac.compare_digest(received, expected):
        abort(401)

    event = request.get_json()
    # event["eventType"], event["data"], event["sentAt"], event["deliveryAttempt"], event["pendingRetries"]
    return "", 200
```