# Securing Webhooks

To ensure the authenticity of event requests, Mailgun signs them and posts the signature alongside the webhook's event-data.

A signature takes the following form:


```JSON
{
    "token": "e0b5477167110d68991efc6b9f89f0a11066af27834600e123",
    "timestamp": "1770920772",
    "signature": "12d99f5a15355c180971bed7494d578b093c958f57766f3fe750761baed12345"
}
```

When an event occurs for a domain belonging to a subaccount, the signature payload will include a `parent-signature` field to identify the primary account. This is so that the receiving server does not have to account for each individual subaccount signature and can instead reference one primary/parent signature to verify the authenticity of the webhook.

Example:


```JSON
{
    "token": "e0b5477167110d68991efc6b9f89f0a11066af27834600e123",
    "timestamp": "1770920772",
    "signature": "12d99f5a15355c180971bed7494d578b093c958f57766f3fe750761baed12345",
    "parent-signature": "13d99f5a15355c180971bed7494d578b093c958f57766f3fe750761baed54321"
}
```

| **Parameter** | **Type** | **Description** |
|  --- | --- | --- |
| token | string | Randomly generated string with length of 50. |
| timestamp | int | Number of seconds passed since January 1, 1970. |
| signature | string | String with hexadecimal digits generated by an HMAC algorithm |
| parent-signature | string | Same as signature; only included when event happens on a subaccount |


To verify the webhook originated from Mailgun, you will need to:

- Concatenate timestamp and token values together with no separator.
- Encode the resulting string with the HMAC algorithm, using your Webhook Signing Key as a key and SHA256 digest mode.
- Compare the resulting hexdigest to the signature. If they do not match then the webhook is not from Mailgun!
- Optionally, you can cache the token value locally and not honor any subsequent request with the same token. This will prevent replay attacks.
- Optionally, you can check if the timestamp is not too far from the current time.
  - There can be delays in webhook processing that are outside of Mailgun's control so you don't want to be too aggressive with this check.


To visualize, here's a sample NodeJS snippet that checks the Webhook signature:


```javascript
const crypto = require('crypto')

const verify = ({ signingKey, timestamp, token, signature }) => {
    const encodedToken = crypto
        .createHmac('sha256', signingKey)
        .update(timestamp.concat(token))
        .digest('hex')

    return (encodedToken === signature)
}
```

#### TLS Client Authentication

If your receiving server uses valid TLS configuration - Mailgun includes a TLS client certificate with our Webhook requests.
This allows customers to use transport-level validation by inspecting the TLS certificate provided in the request to perform
additional validation for whether the request originated from Mailgun.

How it works:

- Mailgun includes our TLS client certificate in webhook requests, which is owned and managed by Mailgun and is issued by DigiCert
- Your server should be configured to only accept requests from clients with valid TLS certificates issued by a trusted CA (e.g., DigiCert)
- Once you've confirmed the certificate is valid: then verify that the certificate's Common Name (CN) is `webhooks.mgsend.net`


Warning!
We do not support validation of server certificates issued by custom CA’s on Mailgun's side before sending, i.e mutual TLS (mTLS)

You can download the current certificate for inspection from our [US](https://api.mailgun.net/v1/webhooks/tls_cert) or [EU](https://api.eu.mailgun.net/v1/webhooks/tls_cert) API.