Webhook Security

Every webhook request is signed by Altery using a private key. You verify the signature using the corresponding public key provided during onboarding. This guarantees the request genuinely came from Altery and was not tampered with.

Request Headers

Every webhook includes the following security headers:

HeaderDescription
X-SignatureBase64-encoded RSA-SHA256 signature of the payload
X-TimestampUTC timestamp of when the webhook was sent (Unix seconds)
X-Key-IdID of the signing key used — use this to verify the key is current

Verification Steps

Step 1 — Read the headers

Extract X-Signature, X-Timestamp, and X-Key-Id from the incoming request.

Step 2 — Check the timestamp


Reject the webhook if X-Timestamp is older than 5 minutes. This protects against replay attacks where a valid request is captured and resent.

Step 3 — Reconstruct the signed payload

Concatenate the timestamp and raw request body:

X-Timestamp + "." + raw_body
🚧

Use the raw body bytes — do not parse or reformat the JSON first. Any whitespace change will invalidate the signature.

Step 4 — Verify the signature

Verify the RSA SHA-256 signature (PKCS#1 v1.5) against the reconstructed payload using your public key.

const crypto = require('crypto');

const publicKey = '... your public key from Altery onboarding ...';
const xSignature = '... header X-Signature ...';
const xTimestamp = '... header X-Timestamp ...';
const rawBody = '... exact webhook request body string ...';

// Step 2 - reject stale webhooks
const age = Date.now() - Number(xTimestamp);
if (age > 5 * 60 * 1000) {
  throw new Error('Webhook rejected: timestamp too old');
}

// Step 3 - reconstruct signed payload
const data = Buffer.from(`${xTimestamp}.${rawBody}`, 'utf8');
const signature = Buffer.from(xSignature, 'base64');

// Step 4 - verify
const isValid = crypto.verify(
  'RSA-SHA256',
  data,
  {
    key: publicKey,
    padding: crypto.constants.RSA_PKCS1_PADDING
  },
  signature
);

if (!isValid) {
  // Return HTTP 400 and log for investigation
  throw new Error('Webhook rejected: invalid signature');
}

Step 5 — Handle the result

ResultAction
isValid = trueProcess the webhook normally, return HTTP 200
isValid = falseReturn HTTP 400, do not process, log for investigation
Timestamp too oldReturn HTTP 400, do not process

Getting Your Public Key

Your public key is provisioned by Altery during onboarding and stored against your integration. Use X-Key-Id to confirm which key was used — if the ID does not match your current key, contact your account manager as your key may have been rotated.