Skip to main content
Your webhook endpoint is public: anyone who knows the URL can send you forged requests. Exoid signs every request with HMAC-SHA256 and you have to verify the signature before trusting the content.

How Exoid signs requests

  • Exoid signs the unparsed request body (the raw body, byte for byte) with HMAC-SHA256, using the campaign’s Webhook Secret.
  • The signature travels in the X-Signature header, in the format sha256=<hex>.
  • Your endpoint has to recreate the signature and compare it with the one received. If they don’t match, reject the request.

Verification procedure

1

Read the raw body

Receive the body as an unparsed Buffer, without applying any JSON middleware.
2

Extract the header

Take the value of X-Signature from the incoming request.
3

Recompute the HMAC

Compute HMAC-SHA256 of the raw body with the campaign’s Webhook Secret and format the result as sha256=<hex>.
4

Compare

If the computed signature doesn’t match the one received, answer 401 and stop processing.
5

Process the payload

Only at this point run JSON.parse on the body and use the data.

Node.js example (Express)

verify-signature.js
You have to compute the HMAC on the unparsed body. If you run JSON.parse (or middleware like express.json()) before verifying and then re-serialize the object, the bytes change — spacing, key order, escaping — and the comparison fails even on perfectly legitimate requests. Always use the raw Buffer, for example with express.raw.
Compare signatures in constant time (for example with crypto.timingSafeEqual) so you don’t leak information through response timing. And never write the Webhook Secret to your application logs.
An endpoint that answers 401 gets no retries: 401 is a permanent error. Make sure verification is correct before going to production, otherwise you lose events.

Next steps

Payload reference

The TypeScript data model, the response types and a sample payload.

Configuration

Where you set the secret and how to fire a test request.