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

# Checking Signatures

> [!WARNING]
>
> In a production setting, it is important to verify signatures sent as part of the webhook payloads. Verifying these signatures ensures that the payloads were sent by One Codex and not a malicious third party.

In addition to the webhook payload body, we include a custom `X-OneCodex-Signature` HTTP header with all delivered webhooks. These signatures are generated using a hash-based message authentication code (HMAC) with SHA-256. Here's an example header:

```
X-OneCodex-Signature: t=1492774577c v1=d929ba98ac0e56ff425f9b8ed7c7ab631dc680f9ea80ce2f604cc75580a63b53
```

Where `t=` provides a Unix timestamp and `v1=` provides the v1 signature (currently the only signature scheme). The signature uses a webhook secret (defaults to the API key for your account) to sign the POST payload body and timestamp. To verify the signature of the payload, you need to:

1. Extract the timestamp and signature from the headers
2. Concatenate the the timestamp and request payload with a `.` to generate a signed payload
3. Determine the expected signature; and finally
4. Verify that the expected signature matches the received signature

Some brief Python 3 code for validating the signature is included for demonstration purposes below:

```python python theme={null}
import hashlib
import hmac
import json

from flask import request

# Parse the request using your web framework of choice.
# Note that the entire body of the request is the payload
# and that you may need to parse the raw request body
# vs. any loaded JSON in a different language or framework
# (this code assumes a Python Flask request object).
payload = request.json
header = request.headers.get("X-OneCodex-Signature")

# 1. Extract the timestamp and signature
timestamp_part = header.split(" ")[0]
signature_part = header.split(" ")[1]
if not timestamp_part.startswith("t=") or not signature_part.startswith("v1="):
    raise Exception("Bad signature header format")

timestamp = int(timestamp_part.split("=")[1])
signature = signature_part.split("=")[1]

# 2. Generate a concatenated signed payload
signed_payload = "%d.%s" % (timestamp, payload)

# 3. Compute the SHA256 hash of your secret
secret = hashlib.sha256("YOUR_WEBHOOK_SECRET".encode("utf-8")).hexdigest().encode("utf-8")

# 4. Determine the expected signature
expected_signature = hmac.new(
    secret,
    signed_payload.encode(),
    digestmod=hashlib.sha256
).hexdigest()

# 4. Verify that the expected signature matches
assert expected_signature == signature
```

*Note: We use a similar format to [Stripe](https://stripe.com/docs/webhooks/signatures) for our payload signatures (they're the same except Stripe delimits the signed payload and timestamp with a comma vs. a space). See their [rich documentation](https://stripe.com/docs/webhooks/signatures) for additional details on why payload signatures are important and related webhook best practices.*

> [!NOTE]
>
> In the near future, we plan to add support for parsing `Event` objects and verifying the signatures from a webhook payload in our [onecodex](https://github.com/onecodex/onecodex) Python library. This will offer an easy, one line mechanism for verifying payload POST bodies sent by our platform.


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