Integrations

Webhooks

Push signed JSON payloads to your own endpoint whenever a signal fires or a digest is ready.

Setup

In Settings → Integrations, add your Webhook URL and enable it. Hunch generates a signing secretshown in the same panel, you'll use it to verify every request. Use Test to send a sample payload.

Webhook URLs must be public https endpoints. Internal, loopback, and private addresses are rejected to prevent SSRF.

Events

Every delivery has an event field. The event types are:

  • signal.detected, a signal fired on one of your accounts.
  • daily.digest, the once-daily summary.

Payload shape

All payloads share a common envelope:

Envelope
{
  "event": "signal.detected",
  "workspace_id": "d347bc8c-1288-439b-8a45-57998aff0c6c",
  "timestamp": "2026-07-03T18:34:07.971Z",
  "data": { /* event-specific */ }
}

signal.detected

data
{
  "account": {
    "id": "e7550358-d243-491d-9895-98005afe62bc",
    "name": "Ramp",
    "domain": "ramp.com"
  },
  "signal": {
    "id": "3f9a1c22-0b7e-4a1d-9c2e-6d4b8f1e2a90",
    "name": "New Revenue Leader"
  },
  "headline": "New CRO hired, pipeline build begins",
  "alpha": 88,
  "run_id": "8c1e5b90-2a44-4f3e-9b21-7a05843f021c"
}

daily.digest

data
{
  "total_signals": 12,
  "total_suggested": 5,
  "signals": [
    {
      "accountId": "acc_...",
      "accountName": "Ramp",
      "accountDomain": "ramp.com",
      "logoUrl": "https://...",
      "signalName": "New Revenue Leader",
      "headline": "New CRO hired, pipeline build begins",
      "alpha": 88
    }
  ],
  "suggested": [
    { "name": "Acme", "domain": "acme.com", "industry": "SaaS", "employees": 400 }
  ]
}

Verifying signatures

Each request carries two headers:

X-Signal-Timestampstringoptional

Unix timestamp (seconds) when the payload was signed.

X-Signal-Signaturestringoptional

Hex HMAC-SHA256, prefixed with sha256=.

Compute the expected signature as HMAC-SHA256(secret, `${timestamp}.${raw_body}`)and compare it to the header. Reject the request if they don't match.

Node.js verification
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(req, rawBody, secret) {
  const ts = req.headers["x-signal-timestamp"];
  const sig = String(req.headers["x-signal-signature"]).replace(/^sha256=/, "");
  const expected = createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");
  return timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"));
}
Verify against the raw request body, before any JSON parsing or re-serialization, a re-encoded body will not match the signature.

Delivery behavior

  • Requests time out after 10 seconds; a slow endpoint won't hold up your alerts.
  • Non-2xx responses are logged. Design your receiver to be idempotent.
  • Which events fire is governed by your notification content toggles.