Webhook signature verification
Every webhook delivery includes a Socialspool-Signature header that you verify using your webhook secret. This proves the request genuinely came from SocialSpool and has not been tampered with.
Signature format
The Socialspool-Signature header contains two comma-separated key-value pairs:
t=1717200000,v1=abc123def456...t: the Unix timestamp of when the signature was generated.v1: the HMAC-SHA256 signature hex digest.
Verification steps
- Extract
tandv1from theSocialspool-Signatureheader. - Optionally check that
tis within your tolerance window (recommended: 5 minutes) to prevent replay attacks. - Construct the signed payload:
${t}.${rawBody}whererawBodyis the exact request body as a UTF-8 string. - Compute
HMAC-SHA256of the signed payload using your webhook secret as the key. - Compare the computed digest to
v1using a constant-time comparison function.
Node.js example
const crypto = require("node:crypto")
function verifySocialSpoolSignature({
rawBody,
signatureHeader,
secret,
}) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((part) => part.split("="))
)
const timestamp = parts.t
const expected = parts.v1
const signedPayload = `${timestamp}.${rawBody}`
const actual = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex")
return crypto.timingSafeEqual(
Buffer.from(actual),
Buffer.from(expected)
)
}Webhook secret
Each webhook endpoint has its own secret, generated when the endpoint is created. The secret is shown once and stored as a SHA-256 hash. If you lose the secret, create a new webhook endpoint and update your consumer.