Verifying webhooks
Each destination gets its own signing secret when it is created in the portal (it starts with whsec_). Use it to verify the x-outpost-signature header sent with every delivery.
Verifying the signature
Section titled “Verifying the signature”- Take the raw request body exactly as received, before parsing or re-serialising it.
- Compute
HMAC-SHA256over those bytes using your destination’s signing secret, hex-encoded. - The header is
v0=<signature>. Compare using a constant-time comparison. - If a secret is being rotated, the header carries both signatures, comma-separated —
v0=<new>,<old>. Accept the request if any of them matches a secret you hold. - Reject anything that fails verification.
Example — Node.js
Section titled “Example — Node.js”import { createHmac, timingSafeEqual } from 'node:crypto'
function isFromLevno(rawBody, signatureHeader, secret) { const expected = createHmac('sha256', secret).update(rawBody).digest('hex') const received = signatureHeader.replace(/^v0=/, '').split(',')
return received.some( (signature) => signature.length === expected.length && timingSafeEqual(Buffer.from(signature), Buffer.from(expected)), )}Keep credentials separate
Section titled “Keep credentials separate”REST API keys authenticate requests a partner sends to Levno. A webhook signing secret authenticates Events requests Levno sends to the partner. Do not reuse one credential for both directions.