Skip to content
snapcasterDevelopers

Webhook signing

Every webhook Delivery carries X-Snapcaster-Timestamp as Unix seconds and X-Snapcaster-Signature as sha256=<lowercase hex>. The signed message is the timestamp concatenated directly with the exact raw request body bytes. There is no delimiter.

Verify before parsing JSON. Re-serializing a parsed body changes its bytes and invalidates the signature.

Node.js raw-body signature verification
const crypto = require('node:crypto');
function verifySnapcasterWebhook(rawBody, headers, signingSecret) {
const timestamp = headers['x-snapcaster-timestamp'];
const signature = headers['x-snapcaster-signature'];
if (typeof timestamp !== 'string' || typeof signature !== 'string') return false;
if (!/^\d+$/.test(timestamp) || !/^sha256=[a-f0-9]{64}$/i.test(signature)) return false;
const timestampSeconds = Number(timestamp);
if (!Number.isSafeInteger(timestampSeconds)) return false;
if (Math.abs(Date.now() / 1000 - timestampSeconds) > 300) return false;
const expected = Buffer.from(
'sha256=' +
crypto.createHmac('sha256', signingSecret).update(timestamp).update(rawBody).digest('hex'),
);
const received = Buffer.from(signature);
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Use an exact-byte body facility such as express.raw() for the webhook route. Only after verification succeeds should the handler parse JSON and deduplicate on event_id.

  1. Capture the raw body bytes.
  2. Reject a missing or malformed timestamp or signature.
  3. Reject a timestamp outside the five-minute replay window.
  4. Recompute HMAC-SHA256 over timestamp || rawBody with the environment’s per-Platform Signing secret.
  5. Compare the complete prefixed signature in constant time.
  6. Parse JSON and deduplicate the Delivery on event_id.

The Signing secret is different in sandbox and production. Do not use a Seller’s Marketplace API key for webhook verification.

Need help?

Email Marketplace support with this page and the sandbox environment prefilled.

Never include an API key or Signing secret in the message.