Skip to main content

Webhooks & Events

Receive real-time HTTP POST notifications for simulation results, quota alerts, and workspace events.

Pro & Business feature — coming soon

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.

Webhooks are currently in closed beta. We will roll out the UI dashboard to Business workspaces in a future release. You can configure your endpoint via the Convex dashboard or support for now.

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.

Node.js — verify signature
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;
}
Always verify the signature before processing a webhook payload. Never trust the event data if signature verification fails — reject with HTTP 401.

Retry policy

If your endpoint returns a non-2xx status code or times out (5 s limit), Lumis retries with exponential backoff:

AttemptDelay after failure
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes
FinalWebhook 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.passed

Fired after every simulation that evaluates to passed. Useful for downstream workflows that trigger on compliance clearance.

Pro, Business
{
  "event": "simulation.passed",
  "transactionId": "tx_9p2kx8f",
  "rulesetId": "jx7abc123",
  "rulesetName": "US KYC / FinCEN AML",
  "stepsExecuted": 6,
  "timestamp": 1714000001234
}
simulation.failed

Fired when a simulation is blocked by a critical failure. Useful for routing alerts to PagerDuty or Slack.

Pro, Business
{
  "event": "simulation.failed",
  "transactionId": "tx_4xqr7",
  "rulesetId": "jx7abc123",
  "failedNodeId": "aml_threshold",
  "timestamp": 1714000009871
}
quota.warning

Fired when monthly simulation usage hits 80%, 90%, and 95% of the tier limit. Metered tiers do not receive this event.

Pro, Business
{
  "event": "quota.warning",
  "workspaceId": "ws_k29xp",
  "used": 800,
  "limit": 1000,
  "percentUsed": 80,
  "timestamp": 1714000015000
}
quota.exceeded

Fired the moment a workspace hits its hard monthly limit. Further API calls will return QUOTA_EXCEEDED until the next reset.

Pro, Business
{
  "event": "quota.exceeded",
  "workspaceId": "ws_k29xp",
  "tier": "free",
  "limit": 1000,
  "timestamp": 1714000022000
}

Example handler (Express)

webhook-handler.ts
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);
  }
});