Skip to content

Webhooks Overview

LMIF webhooks notify your platform of events in real-time, allowing you to respond immediately to identity protection changes.

Instead of polling the API, webhooks push events to you:

  • Real-time: Know immediately when identities are boxed
  • Efficient: No wasted API calls
  • Reliable: Automatic retries on failure
  • Secure: Signed payloads prevent tampering
const webhook = await lmif.webhooks.create({
url: 'https://yoursite.com/webhooks/lmif',
events: ['box.created', 'grace_period.started', 'license.approved'],
secret: 'your-webhook-secret' // Optional, we'll generate one if not provided
});
console.log('Webhook secret:', webhook.secret);
// Store this securely - you'll need it to verify signatures
import express from 'express';
import { LMIFClient } from '@lookmaimfamous/lmif';
const app = express();
const lmif = new LMIFClient({ apiKey: process.env.LMIF_API_KEY });
app.post('/webhooks/lmif', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-lmif-signature'];
const payload = req.body;
// Verify signature
if (!lmif.webhooks.verify(payload, signature, process.env.LMIF_WEBHOOK_SECRET)) {
console.error('Invalid webhook signature');
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle the event
switch (event.type) {
case 'box.created':
handleBoxCreated(event.data);
break;
case 'grace_period.started':
handleGracePeriodStarted(event.data);
break;
// ... handle other events
}
// Acknowledge receipt
res.status(200).send('OK');
});

Always verify webhook signatures to ensure events are from LMIF:

// Using the SDK
const isValid = lmif.webhooks.verify(payload, signature, secret);
// Manual verification
import crypto from 'crypto';
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expectedSignature}`)
);
}

All webhooks follow this format:

{
"id": "evt_abc123",
"type": "box.created",
"createdAt": "2024-01-15T10:00:00Z",
"data": {
// Event-specific data
},
"meta": {
"apiVersion": "v1",
"webhookId": "wh_xyz789"
}
}
CategoryEvents
Boxbox.created, box.updated, box.deleted
Violationviolation.detected, violation.resolved, violation.appealed
Grace Periodgrace_period.started, grace_period.reminder, grace_period.ending, grace_period.expired, grace_period.resolved
Licenselicense.requested, license.approved, license.denied, license.expiring, license.expired, license.revoked
Detectiondetection.match_found, detection.scan_complete

See Event Types for detailed event documentation.

Failed webhook deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 hours
724 hours

After 7 attempts, the webhook is marked as failed. You can manually retry from the dashboard.

Return a 2xx status code within 30 seconds. Process events asynchronously:

app.post('/webhooks/lmif', async (req, res) => {
// Verify signature first
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
// Acknowledge immediately
res.status(200).send('OK');
// Process asynchronously
const event = JSON.parse(req.body);
await processEventAsync(event);
});

Webhooks may be delivered more than once. Use the event ID for idempotency:

async function handleEvent(event) {
// Check if already processed
const processed = await db.events.findOne({ eventId: event.id });
if (processed) {
console.log('Event already processed:', event.id);
return;
}
// Process and record
await processEvent(event);
await db.events.insert({ eventId: event.id, processedAt: new Date() });
}

Set up alerting for webhook failures:

app.post('/webhooks/lmif', (req, res) => {
try {
// Process webhook
} catch (error) {
// Alert on failure
alertOps('Webhook processing failed', error);
// Still return 200 to prevent retries if it's a processing error
res.status(200).send('OK');
}
});
await lmif.webhooks.test({
eventType: 'box.created',
endpoint: 'https://yoursite.com/webhooks/lmif'
});

Use a tunneling service for local testing:

Terminal window
# Using ngrok
ngrok http 3000
# Your webhook URL becomes:
# https://abc123.ngrok.io/webhooks/lmif
const webhooks = await lmif.webhooks.list();
await lmif.webhooks.update('wh_xyz789', {
events: ['box.created', 'box.updated'] // Change subscribed events
});
await lmif.webhooks.delete('wh_xyz789');
const newSecret = await lmif.webhooks.rotateSecret('wh_xyz789');
// Update your code with the new secret