Skip to main content
Whenever a webhook is called, an X-Syntage-Signature header is sent to help verify it. Its value has the form t=<timestamp>,s=<signature>. Syntage generates the signature using a hash-based message authentication code (HMAC) with SHA-256 using the signingSecret as key, and sends it as a lowercase hexadecimal string. Syntage also sends the same value in the X-Satws-Signature header for integrations built before the header was renamed. New integrations should read X-Syntage-Signature. To verify the signature you can do the following steps:

Step 1: Extract the timestamp and signature from the header

Split the header, using the , character as the separator. The value for the prefix t corresponds to the timestamp and s corresponds to the signature.

Step 2: Prepare the signed payload

Create the signed payload by concatenating these values without spaces:
  • the timestamp as a Unix timestamp string, for example "1656569160"
  • the . character
  • the raw request body exactly as received

Step 3: Determine the expected signature

Compute an HMAC with the SHA-256 hash function. Use the endpoint’s signing secret as the key and the signed payload as the message.

Step 4: Compare the signatures

Compare the signature in the header to the expected signature. To protect against timing attacks, use a constant-time comparison. You should also reject timestamps outside your tolerance window.

Examples

These examples assume you pass the raw request body exactly as received. Do not parse and re-serialize the JSON before computing the signature. In the JavaScript and Python examples, pass the value of the X-Syntage-Signature header as the signature header.

PHP

JavaScript / TypeScript

Python

Checking the Content-Digest header

Each delivery also includes a Content-Digest header, as defined in RFC 9530. It contains the SHA-256 digest of the raw request body, encoded in base64 and wrapped in colons:
To check it, compute the SHA-256 digest of the raw request body, encode the binary digest in standard base64, and compare the result with the value between the colons. The digest confirms that the body was not altered or truncated in transit. It does not prove that the request came from Syntage, because anyone can compute a digest, so always validate the signature as described above. The signature already covers the full body, which makes checking the digest optional.