Skip to main content

Signature Verification

Every webhook request includes two headers:

HeaderContents
x-autorev-signatureHMAC-SHA256 signature, e.g. sha256=abc...
x-autorev-timestampTimestamp of the delivery

The signature is an HMAC-SHA256 digest computed with your subscription's signing_secret (the whsec_... value returned once at creation) over the string:

timestamp + "." + rawBody

Two rules that prevent broken or insecure verifiers:

  1. Sign over the raw request body, the exact JSON string as received, not a re-serialized version of the parsed object. Re-serializing can reorder keys or change whitespace and the signature will not match.
  2. Compare with timingSafeEqual, never ==. A plain string comparison leaks timing information.

Node.js example

const crypto = require('crypto')

function verifyWebhook(req, signingSecret) {
const timestamp = req.headers['x-autorev-timestamp']
const sigHeader = req.headers['x-autorev-signature'] // "sha256=abc..."
const body = req.rawBody // raw JSON string, NOT the parsed object

const expected = 'sha256=' + crypto
.createHmac('sha256', signingSecret)
.update(`${timestamp}.${body}`)
.digest('hex')

const a = Buffer.from(sigHeader)
const b = Buffer.from(expected)
return a.length === b.length && crypto.timingSafeEqual(a, b)
}

Note the length check before crypto.timingSafeEqual: the function throws if the buffers differ in length, so guard it and treat a length mismatch as a failed verification.

Using it in a webhook handler

app.post('/webhooks/autorev', (req, res) => {
if (!verifyWebhook(req, process.env.AUTOREV_SIGNING_SECRET)) {
return res.status(401).send('Invalid signature')
}
// Process event...
res.status(200).json({ received: true })
})

Your framework must expose the raw body (for example, an Express raw-body middleware that stores it on req.rawBody) so the digest is computed over exactly the bytes AutoRev sent.

Where the secret comes from

Only the Webhook Subscriptions API returns a signing secret, and only once, at creation. The legacy per-receptionist webhook_url receives events too, but its secret is not retrievable via the API, so signature verification is only practical on subscriptions. If you lose a secret, delete the subscription and create a new one.