🚀 VaultsPay API v1 is live. See what's new →
WebhooksSignature Verification

Signature Verification

Every webhook delivery is signed with an HMAC-SHA256 signature computed over the raw request body, using your endpoint’s signing secret as the key.

⚠️

Always verify signatures before trusting an event. Never parse the body first.

Headers

HeaderContents
VaultsPay-Signaturet=<timestamp>,v1=<hex_signature>
VaultsPay-Event-IdThe unique event ID — convenient for dedup.

The t value is the Unix timestamp at which VaultsPay signed the payload. The v1 value is the HMAC signature.

Algorithm

signed_payload = `${t}.${raw_request_body}`
v1 = HMAC-SHA256(signing_secret, signed_payload).hex()

You should:

  1. Split the header to extract t and v1.
  2. Compute the HMAC yourself using the raw body (do not re-encode).
  3. Compare in constant time using crypto.timingSafeEqual (or your language’s equivalent).
  4. Reject events with a t older than 5 minutes to avoid replay attacks.

Example

import crypto from 'crypto'
 
function verifySignature(rawBody, header, secret) {
  if (!header) return false
  const parts = Object.fromEntries(header.split(',').map(kv => kv.split('=')))
  const { t, v1 } = parts
  if (!t || !v1) return false
 
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
 
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex')
 
  return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
}

Rotating the signing secret

You can rotate an endpoint’s signing secret from the dashboard. There’s a 24-hour grace period where both the old and new secrets are valid, to let you deploy.