Webhooks (Signed Events)¶
Hostiva sends event notifications to your SaaS backend (Airfree Cloud) using signed webhooks.
Headers¶
X-Hostiva-Event: <event_name>X-Hostiva-Timestamp: <unix_epoch_seconds>X-Hostiva-Signature: sha256=<hex_hmac>
Signature algorithm:
HMAC_SHA256(webhook_secret, timestamp + "." + raw_body)
Replay protection (required)¶
- Reject if timestamp is older than 5 minutes
- Store processed
event_idfor 24 hours
Laravel verification example¶
$secret = env('HOSTIVA_WEBHOOK_SECRET');
$timestamp = request()->header('X-Hostiva-Timestamp');
$signature = request()->header('X-Hostiva-Signature'); // sha256=...
$raw = request()->getContent();
$base = $timestamp . '.' . $raw;
$expected = 'sha256=' . hash_hmac('sha256', $base, $secret);
if (!hash_equals($expected, $signature)) abort(401, 'Invalid signature');
if (abs(time() - intval($timestamp)) > 300) abort(401, 'Stale timestamp');
Node.js verification example¶
import crypto from "crypto";
export function verifyHostivaWebhook(req, secret) {
const timestamp = req.header("X-Hostiva-Timestamp");
const signature = req.header("X-Hostiva-Signature"); // sha256=...
const rawBody = req.rawBody ?? "";
const base = `${timestamp}.${rawBody}`;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(base).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature || ""));
if (!ok) throw new Error("Invalid signature");
if (Math.abs(Date.now()/1000 - parseInt(timestamp, 10)) > 300) {
throw new Error("Stale timestamp");
}
return true;
}