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:
| Header | Description |
|---|---|
X-Signature | Base64-encoded RSA-SHA256 signature of the payload |
X-Timestamp | UTC timestamp of when the webhook was sent (Unix seconds) |
X-Key-Id | ID 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
| Result | Action |
|---|---|
isValid = true | Process the webhook normally, return HTTP 200 |
isValid = false | Return HTTP 400, do not process, log for investigation |
| Timestamp too old | Return 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.
