HubSEOhubseoDocs
REST API & Webhooks

Webhook Event Streams

Receive signed HMAC-SHA256 HTTP POST notifications when crawls finish or ranking shifts occur.

Webhooks stream real-time events from the Four Systems engine directly into your application, Slack notifier, or n8n workflow.

Supported Events

Event NameDescription
audit.completedTriggered when a scheduled or on-demand crawl finishes
audit.failedTriggered if an audit terminates with an error or DNS failure
ranking.droppedTriggered when a tracked query drops $>3$ positions
ranking.climbedTriggered when a query climbs into striking distance (positions 4–10)
intervention.deployedTriggered when an automated fix deploys via a CMS connector
intervention.rolled_backTriggered when an automated rollback restores prior state
backlink.lostTriggered when a high-authority referring domain disappears
geo_score.changedTriggered when AI engine brand sentiment or citation rate changes

Register a Webhook

POST /api/v1/webhooks

Request Body

{
  "url": "https://api.yourdomain.com/webhooks/hubseo",
  "events": ["audit.completed", "ranking.dropped"],
  "description": "Production Alert Pipeline"
}

Response (201 Created)

{
  "data": {
    "id": "whk_01j7x5...",
    "url": "https://api.yourdomain.com/webhooks/hubseo",
    "events": ["audit.completed", "ranking.dropped"],
    "secret": "whsec_aB1cD2eF3gH4...",
    "is_active": true,
    "created_at": "2026-09-14T16:00:00.000Z"
  }
}

Save Signing Secret

The secret token (whsec_...) is only returned once in the creation response. Store it securely to verify signatures on incoming HTTP payloads.


Verifying Webhook Signatures

HubSEO computes an HMAC-SHA256 signature using your webhook's secret and sends it in the HubSEO-Signature header:

HubSEO-Signature: t=1726329600,v1=5d41402abc4b2a76b9719d911017c592
HubSEO-Event-ID: evt_01j7x6...
HubSEO-Event-Type: audit.completed
HubSEO-Timestamp: 1726329600

Verification Example (Node.js)

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySignature(
  rawBody: string,
  header: string,
  secret: string,
  toleranceSec = 300
): boolean {
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("="))
  );
  const timestamp = parseInt(parts.t, 10);
  const receivedSig = parts.v1;

  if (isNaN(timestamp) || !receivedSig) return false;

  // Prevent replay attacks (5 minute window)
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSec) {
    return false;
  }

  const expectedSig = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(receivedSig, "hex");
  const b = Buffer.from(expectedSig, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}

Testing Webhooks

You can trigger an immediate diagnostic test ping from the Developer Portal at hubseo.dev/dashboard/developers or via API:

POST /api/v1/webhooks/{id}/ping

HubSEO performs a live network dispatch to your endpoint, verifies HMAC delivery, and reports real round-trip latency measured via performance.now().

On this page