Webhooks & Events
Receive real-time HTTP POST notifications for simulation results, quota alerts, and workspace events.
Overview
Webhooks allow your infrastructure to react to Lumis events without polling the API. When an event fires, Lumis sends an HTTP POST to your registered endpoint with a signed JSON payload.
Signature verification
Every webhook delivery includes an X-Lumis-Signature-256 header. This is an HMAC-SHA256 signature of the raw request body, computed with your webhook signing secret.
import { createHmac } from "crypto";
function verifyWebhook(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
// Use timingSafeEqual to prevent timing attacks
return expected === signature;
}Retry policy
If your endpoint returns a non-2xx status code or times out (5 s limit), Lumis retries with exponential backoff:
| Attempt | Delay after failure |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| Final | Webhook delivery marked failed — no more retries |
Respond to webhooks with HTTP 200 immediately and process asynchronously. A slow synchronous handler is the most common cause of false retries.
Event catalog
simulation.passedFired after every simulation that evaluates to passed. Useful for downstream workflows that trigger on compliance clearance.
{
"event": "simulation.passed",
"transactionId": "tx_9p2kx8f",
"rulesetId": "jx7abc123",
"rulesetName": "US KYC / FinCEN AML",
"stepsExecuted": 6,
"timestamp": 1714000001234
}simulation.failedFired when a simulation is blocked by a critical failure. Useful for routing alerts to PagerDuty or Slack.
{
"event": "simulation.failed",
"transactionId": "tx_4xqr7",
"rulesetId": "jx7abc123",
"failedNodeId": "aml_threshold",
"timestamp": 1714000009871
}quota.warningFired when monthly simulation usage hits 80%, 90%, and 95% of the tier limit. Metered tiers do not receive this event.
{
"event": "quota.warning",
"workspaceId": "ws_k29xp",
"used": 800,
"limit": 1000,
"percentUsed": 80,
"timestamp": 1714000015000
}quota.exceededFired the moment a workspace hits its hard monthly limit. Further API calls will return QUOTA_EXCEEDED until the next reset.
{
"event": "quota.exceeded",
"workspaceId": "ws_k29xp",
"tier": "free",
"limit": 1000,
"timestamp": 1714000022000
}Example handler (Express)
import express from "express";
import { createHmac } from "crypto";
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhooks/lumis", (req, res) => {
const signature = req.headers["x-lumis-signature-256"] as string;
const secret = process.env.LUMIS_WEBHOOK_SECRET!;
const expected = createHmac("sha256", secret)
.update(req.body)
.digest("hex");
if (expected !== signature) {
return res.status(401).send("Invalid signature");
}
// Respond immediately — process async
res.status(200).send("OK");
const event = JSON.parse(req.body.toString());
if (event.event === "simulation.failed") {
// Alert on-call, open a ticket, update dashboard...
console.error("Compliance failure:", event.failedNodeId);
}
});