Webhooks

Verify signatures

Every request is signed with your endpoint secret. Check it before you trust the body.

Headers

X-Signature
Hex HMAC-SHA256 of {X-Timestamp}.{raw body}, keyed with the endpoint secret.
X-Timestamp
Unix seconds when this attempt was sent.
X-Event-Id
The envelope id.
X-Event-Type
The envelope type.

Verify

  1. Refuse a timestamp more than 5 minutes from your clock. This also stops replays.
  2. Compute the HMAC over the raw bytes, before parsing the JSON.
  3. Compare in constant time.
Verify a request
const crypto = require("node:crypto")

const TOLERANCE_SECONDS = 300

function isAuthentic(secret, rawBody, signature, timestamp) {
  if (!/^\d+$/.test(timestamp)) return false
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest("hex")
  const a = Buffer.from(expected)
  const b = Buffer.from(signature)
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}