Skip to content
Documentation
Open app
Documentation/Webhooks

Verify signatures

Verify each webhook’s origin and integrity before processing it.

On this pageDelivery headersSignature calculationNode.js / TypeScript exampleProcess a verified message

Delivery headers

Each delivery is a POST request to your registered URL.

HTTP headers
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.

Signature
HMAC-SHA256(secret, "${t}.${body}")

Node.js / TypeScript example

verify-kawlya-signature.ts
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