# Webhook signature

> A keyed hash proving that a webhook came from the platform and was not altered. How to verify the CryptoLike X-Signature header over the raw body.

Source: https://cryptolike.net/glossary/webhook-signature
Updated: 2026-09-25

A keyed hash sent in a request header that proves an event came from the holder of a shared secret and that its body was not changed on the way.

## How it works in CryptoLike

CryptoLike signs every webhook with the secret of your endpoint: `X-Signature` is `sha256=` followed by the hex HMAC-SHA256 of `X-Timestamp`, a dot and the raw request body. Your server computes the same value, compares it with a constant-time comparison and rejects the request if they differ or if `X-Timestamp` is more than 300 seconds away from its clock in either direction — that stops a captured request from being replayed.

Verify the raw bytes as received, never re-serialised JSON: key order and whitespace may differ from what your parser would produce. A retried event carries the same body with a fresh `X-Timestamp` and `X-Signature`. The secret is generated for you (64 hex characters) or set by you (16–256 printable ASCII characters without spaces); it is shown once, on creation and on rotation, stays on your server and can be rotated in the cabinet at any time.

The CryptoLike SDKs for TypeScript, Python, Go and PHP verify a webhook in one call, with the constant-time comparison and the timestamp tolerance built in; test vectors, including requests your verifier must reject, are published with the [webhook contract](https://cryptolike.net/docs/webhooks). API requests are signed the other way round, with your API key — see [API authentication](https://cryptolike.net/docs/auth).

## Example

A CryptoLike signature check needs four inputs: the endpoint secret, the `X-Timestamp` header, the raw body bytes and the `X-Signature` header. In Python it is a few lines with the standard library; call it before you parse or store the event:

```python
import hmac, hashlib

def verify(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```
