Skip to content
Back to Docs

Events

Webhooks

Receive HMAC-signed real-time notifications when devices are identified, bots detected, or risk changes.

Creating webhooks via API

Register endpoints with POST /api/tenant/webhooks (OpenAPI: POST /api/webhooks). You can also configure webhooks in the dashboard.

create-webhook.ts
import { FingerprintServerClient } from '@lightningresearch/node-sdk';

const client = new FingerprintServerClient({ apiKey: process.env.LRDEFENDER_API_KEY! });

const webhook = await client.createWebhook({
  url: 'https://api.example.com/webhooks/lrdefender',
  events: ['device.identified', 'bot.detected', 'fraud.high_risk'],
  name: 'Production alerts',
});

// Save webhook.secret (whsec_...) — shown only once on creation
console.log(webhook.webhookId, webhook.secret);

Event types

device.identified
device.new
device.updated
bot.detected
anomaly.detected
fraud.high_risk
compliance.consent_given
compliance.deletion_requested
experiment.completed
tenant.limit_exceeded

Subscribe to specific events or use ['*'] for all events.

Signature verification

LRDefender signs the raw JSON body with HMAC-SHA256 in the X-Webhook-Signature header. Two formats are supported:

  • Plain hex: HMAC-SHA256(secret, payload)
  • Timestamped: t=<unix>,v1=<hex> where the signed message is "<t>.<payload>"
webhook-node.ts
import express from 'express';
import { verifyWebhookSignature } from '@lightningresearch/node-sdk/webhook';

app.post('/webhooks/lrdefender', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('x-webhook-signature') ?? '';
  const ok = verifyWebhookSignature({
    payload: req.body,
    signature,
    secret: process.env.LR_WEBHOOK_SECRET!,
    tolerance: 300,
  });
  if (!ok) return res.status(401).send('Invalid signature');

  const event = JSON.parse(req.body.toString());
  console.log(event.type, event.data);
  res.sendStatus(200);
});
webhook-python.py
from lightningresearch import verify_webhook_signature

def handle_webhook(payload: bytes, signature: str, secret: str) -> bool:
    return verify_webhook_signature(payload, signature, secret)
webhook.go
if err := lrdefender.VerifyWebhookSignature(payload, []byte(signature), []byte(secret), 300); err != nil {
    http.Error(w, "Invalid signature", http.StatusUnauthorized)
    return
}
WebhookHandler.java
boolean valid = WebhookVerifier.verify(payloadBytes, signature, webhookSecret);
if (!valid) throw new SecurityException("Invalid webhook signature");
webhook.php
$valid = WebhookVerifier::verify($payload, $signature, $webhookSecret);
if (!$valid) { http_response_code(401); exit; }
WebhookEndpoint.cs
bool valid = WebhookVerifier.Verify(payload, signature, webhookSecret);
if (!valid) return Results.Unauthorized();

Retry policy

Failed deliveries are queued with exponential backoff. Retry delays: 1s, 5s, 15s, 60s, 300s (5 attempts). Your endpoint must return HTTP 2xx within 30 seconds. Non-2xx responses trigger retries; after exhaustion the delivery is marked failed and visible in the dashboard delivery log.

delivery-payload.json
{
  "type": "bot.detected",
  "webhookId": "wh_abc123",
  "timestamp": "2026-06-09T12:00:00.000Z",
  "data": {
    "deviceId": "a1b2c3d4e5f6789012345678",
    "botScore": 0.94,
    "isBot": true
  }
}

Testing webhooks

  • Use the dashboard Send test event button to fire a sample payload to your endpoint.
  • For local development, tunnel with ngrok or Cloudflare Tunnel and register the public URL as your webhook endpoint.
  • Verify signatures in your handler before processing — reject unsigned or invalid requests with 401.
  • Manually retry failed deliveries from the dashboard delivery log via POST /api/tenant/webhooks/:id/retry.