Skip to content

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_id for 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;
}