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.identifieddevice.newdevice.updatedbot.detectedanomaly.detectedfraud.high_riskcompliance.consent_givencompliance.deletion_requestedexperiment.completedtenant.limit_exceededSubscribe 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.