Verify signatures
Verify each webhook’s origin and integrity before processing it.
On this page
Delivery headersSignature calculationNode.js / TypeScript exampleProcess a verified messageDelivery headers
Each delivery is a POST request to your registered URL.
Content-Type: application/json
X-Kawlya-Event: reservation.created
X-Kawlya-Delivery: evt_3f2504e0-4f89-11e0-b923-1231390215b1
X-Kawlya-Signature: t=1751234567,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Signature calculation
The signature uses HMAC-SHA256 with the subscription secret. The signed message is timestamp t, a dot, and the exact raw request body. Compare signatures in constant time and check the timestamp age.
HMAC-SHA256(secret, "${t}.${body}")
Node.js / TypeScript example
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verifyKawlyaSignature(
header: string,
rawBody: string,
secret: string,
toleranceSec = 300,
): boolean {
const match = header.match(/^t=(\d+),v1=([0-9a-f]{64})$/)
if (!match) return false
const [, timestamp, signature] = match
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSec) {
return false
}
const expected = createHmac('sha256', secret)
.update(timestamp + '.' + rawBody)
.digest('hex')
return timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex'),
)
}
Process a verified message
Reject invalid signatures before taking any business action. After verification, parse the JSON and use the delivery ID to detect duplicates. Retries reuse that ID.
Updated