Webhooks

HMAC-signed event deliveries to your SOAR, SIEM, or Slack channel. Available on Pro+.

Webhooks push Reput.io events to a URL you own, as they happen. Use them to trigger SOAR playbooks, post to Slack, open Jira tickets, or stream high-risk detections into your SIEM without polling.

Webhooks are available on Pro, Team, and Enterprise plans. Upgrade at reput.io/pricing if you're on Free or Starter.

Events

EventWhen it firesPayload summary
lookup.completedAfter every successful /lookup callCount, per-status breakdown, indicators list
ioc.high_risk_detectedWhen a result has risk_level: highSingle IOC with full metadata
subscription.changedWhen your plan changesNew plan + status
api_key.revokedWhen an API key is revokedKey prefix

You pick which events each webhook subscribes to at creation time.

Managing webhooks

All CRUD happens on the dashboard at /settings/integrations. You can also use the API directly:

GET/webhooksJWTPro+
POST/webhooksJWTPro+
PATCH/webhooks/{id}JWTPro+
DELETE/webhooks/{id}JWTPro+
POST/webhooks/{id}/testJWTPro+

You can register up to 20 webhooks per account. The /test endpoint fires a synthetic webhook.test event synchronously so the UI can show you the response code + latency.

Delivery contract

Every event is delivered as a single POST to your URL:

POST https://your-soar.example.com/hooks/reputio
Content-Type: application/json
User-Agent: Reput.io-Webhook/1.0
X-Reputio-Event: ioc.high_risk_detected
X-Reputio-Signature: t=1777666372,v1=adf0d94eaf0a64afb852fdfa3c587b4abe11ffd07d30239e93011c90fa329a8d
X-Reputio-Delivery: 7e3b1a9f08c4d2e6

{
  "event": "ioc.high_risk_detected",
  "timestamp": 1777666372,
  "data": { ... }
}

Timeouts and retries

  • 5 second timeout per delivery
  • No automatic retries in the current MVP — if your endpoint returns a non-2xx or times out, the event is logged (with last_status + last_error visible in the dashboard) and dropped
  • If you need at-least-once delivery, poll /lookup or use the audit log as a backstop

Ordering

Events are fire-and-forget and can arrive out of order. Don't rely on order; instead use X-Reputio-Delivery as an idempotency key and the timestamp field for ordering if you need it.

Signature verification

Every delivery carries an X-Reputio-Signature header in the Stripe-compatible format:

X-Reputio-Signature: t=<unix_seconds>,v1=<hex>

Where v1 is:

HMAC_SHA256(webhook_secret, f"{t}.{raw_body}")

Python

import hmac, hashlib, time

def verify(raw_body: bytes, signature_header: str, secret: str, max_age_s: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    ts = int(parts.get("t", "0"))
    if abs(time.time() - ts) > max_age_s:
        return False  # stale — reject to prevent replay
    expected = hmac.new(
        secret.encode(),
        f"{ts}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Node.js

import crypto from 'crypto'

function verify(rawBody, signatureHeader, secret, maxAgeS = 300) {
  const parts = Object.fromEntries(signatureHeader.split(',').map(p => p.split('=', 2)))
  const ts = parseInt(parts.t, 10)
  if (Math.abs(Date.now() / 1000 - ts) > maxAgeS) return false
  const expected = crypto.createHmac('sha256', secret)
    .update(`${ts}.`).update(rawBody).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}

Always verify timestamps

Reject requests where t is older than 5 minutes even when the signature verifies. Otherwise an attacker who captures a single valid delivery could replay it indefinitely.

Example payloads

lookup.completed

{
  "event": "lookup.completed",
  "timestamp": 1777666372,
  "data": {
    "count": 3,
    "status_counts": { "whitelisted": 2, "unknown": 1 },
    "indicators": ["8.8.8.8", "cloudflare.com", "sketchy.example.tld"]
  }
}

ioc.high_risk_detected

{
  "event": "ioc.high_risk_detected",
  "timestamp": 1777666372,
  "data": {
    "indicator": "bit.ly/xyz",
    "type": "url",
    "status": "whitelisted",
    "risk_level": "high",
    "risk_context": "URL Shortener Link",
    "confidence_score": 30,
    "categories": ["URL Shortener", "Link Service"]
  }
}

Your secret

Each webhook has its own 64-hex-character secret, shown once when you create it. Store it in a secrets manager — we don't keep a retrievable copy. If you lose the secret, delete the webhook and create a new one.