> For the complete documentation index, see [llms.txt](https://docs.360dialog.com/partner/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.360dialog.com/partner/onboarding/webhook-events-and-setup/signature-validation.md).

# Signature Validation

Signature validation is an **optional** security step that ensures:

* **Origin Verification**: Ensures the request is actually from 360Dialog and not a malicious third party.
* **Data Integrity**: Guarantees that the webhook payload has not been modified during transit.

**We recommend partners perform webhook signature validation to enhance their platform security.**

This feature is complementary to the custom webhook URL headers. Custom headers can be used to bypass firewalls or API gateways. Partners can set custom webhook headers and values (e.g., `Authorization: <secret>`, `X-Partner-Auth: <secret>`) while setting their webhook URL.

## Instructions

360Dialog uses the platform secret to sign the webhook event's body with HMAC-SHA256. The signature is inserted to the request's `x-360dialog-signature` header. This header is present in every webhook event sent to the partner webhook URL.

The partner must use the platform secret to generate an HMAC-SHA256 signature of the webhook event's body. The signature must be compared with the signature provided in the `x-360dialog-signature` header **using a** **constant-time algorithm**.

If the signatures are identical, continue processing the webhook. If not identical, reject the request.

{% hint style="danger" %}
**Rotating platform secret**

If the platform secret is leaked, rotate the platform secret immediately.

The platform secret should also be rotated regularly as a best practice. This minimizes the impact of a potential secret compromise.

[See here for instructions.](/partner/partner-hub/integration.md#generate-platform-secret)
{% endhint %}

Make sure to:

1. Pass platform secret to the server using an environment variable or secret manager.
2. Protect the platform secret from being leaked by:
   1. Not sending it to the frontend,
   2. Not making it accessible via API,
   3. Using HTTPS for the webhook URL.\
      HTTP connections are insecure and can be intercepted by third-parties on the same network.
3. Perform signature validation right after receiving a webhook event.
4. Use constant-time comparison to compare the generated signature with the one in the `x-360dialog-signature` header. This prevents timing-based attacks.
5. Use deduplication to protect against replay attacks. A webhook event may be captured by a third-party and replayed to the partner webhook URL.
   1. Extract an identifier from the webhook payload, such as its ID,
   2. Check whether that identifier has already been processed,
   3. If it has already been seen, ignore the webhook event and return an HTTP 200 response,
   4. If it has not been seen, enqueue and process it normally, then store the identifier as processed.

## Implementation

{% stepper %}
{% step %}

### Get Platform Secret

[Get an existing partner secret, or generate a new one by following the instructions here.](/partner/partner-hub/integration.md#generate-platform-secret)
{% endstep %}

{% step %}

### Define Function

The function below signs the payload with the platform secret, then performs a constant-time comparison between the generated signature and the signature received in `x-360dialog-signature` header.

```python
# webhook_security.py

import hmac
import hashlib

def verify_signature(payload, secret, received_signature):
    """
    Verifies 'received_signature' by signing 'payload' with 'secret' using
    HMAC-SHA256. Payload must be the request's raw body in bytes.
    
    Performs constant time comparison between 'received_signature' and generated
    signature.
    
    Returns:
        True if signature is valid, False if invalid.
    """
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected_signature, received_signature)
```

{% endstep %}

{% step %}

### Use Function

Call the function declared in Step 2 right after receiving a webhook event. If there is no x-360dialog-signature header, reject the request with 401. If the function returns false, reject the request with 403.

```python
import os
from fastapi import FastAPI, Request, HTTPException, Response

from webhook_security import verify_signature # Function declared in Step 2

app = FastAPI()

# Retrieve platform secret from environment, or from a secret manager
PARTNER_SECRET = os.environ["PLATFORM_SECRET"]

@app.post("/webhook")
async def webhook(request: Request):
    received_signature = request.headers.get("x-360dialog-signature")
    if not received_signature:
        raise HTTPException(status_code=401)

    raw_body = await request.body()

    if not verify_signature(raw_body, PARTNER_SECRET, received_signature):
        raise HTTPException(status_code=401)

    payload = await request.json()

    # Queue webhook payload processing here, then return 200.
    return Response(status=200)
```

{% endstep %}

{% step %}

### Implement Deduplication

Webhook signatures do not include a timestamp or nonce. This means a valid signed request could be captured and replayed later by an attacker.

To reduce replay risk, partners should implement a deduplication check based on identifiers in the webhook payload, such as message IDs or other event-specific IDs.

#### Recommended approach

* Validate webhook signature first.
* Extract an identifier from the webhook payload, such as its ID.
* Check whether that identifier has already been processed.
* If it has already been seen, ignore the webhook event and return an HTTP 200 response.
* If it has not been seen, enqueue and process it normally, then store the identifier as processed.
  {% endstep %}
  {% endstepper %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.360dialog.com/partner/onboarding/webhook-events-and-setup/signature-validation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
