API Reference
Execute compliance simulations programmatically. All endpoints require an API key.
Authentication
Lumis uses Bearer token authentication. Generate an API key from Settings → API Keys. Keys are shown once at creation — store them securely.
Keys are stored as SHA-256 hashes. Lumis never retains the raw key value.
Authorization: Bearer lmk_live_••••••••••••••••••••••••
HTTP 402 with the header X-Lumis-Reason: trial_expired_keys_paused. Upgrade from the dashboard to restore access without regenerating keys.Base URL
All API calls are routed to the Lumis edge environment via our standard REST endpoints.
The base REST URL for simulations is https://lumiscompliance.com/api/v1/simulate.
POST /api/v1/simulate
Run a compliance simulation. The payload is evaluated through the ruleset DAG and every node result is recorded in the audit trail.
Request body
| Name | Type | Description |
|---|---|---|
rulesetIdrequired | string | The Convex document ID of the target ruleset (e.g. jx7abc123…) |
payloadrequired | object | Arbitrary JSON object representing the transaction or entity to evaluate. Max 64 KB. |
expected_outcome | "pass" | "fail" | Used for coverage analysis. Flags the simulation as matched or mismatched. |
test_suite | string | Group related simulations together under a single test suite ID. |
tags | string[] | Array of arbitrary string tags to annotate this simulation. |
curl -X POST https://lumiscompliance.com/api/v1/simulate \
-H "Authorization: Bearer lmk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{
"rulesetId": "jx7abc123",
"payload": {
"transaction_amount": 1500,
"user_region": "US",
"id_verified": true,
"risk_score": 0.12
}
}'const response = await fetch("https://lumiscompliance.com/api/v1/simulate", {
method: "POST",
headers: {
"Authorization": "Bearer lmk_live_••••••••",
"Content-Type": "application/json"
},
body: JSON.stringify({
rulesetId: "jx7abc123",
payload: {
transaction_amount: 1500,
user_region: "US",
id_verified: true,
risk_score: 0.12,
}
})
});
const result = await response.json();Response
| Name | Type | Description |
|---|---|---|
transactionId | string | Unique ID of this simulation record in your audit log |
status | "passed" | "failed" | "error" | Final verdict of the DAG evaluation |
stepsExecuted | number | Number of DAG nodes that were evaluated |
passedNodeIds | string[] | Array of node IDs that evaluated to passed |
failedNodeId | string | null | The first node that caused a failure, or null on pass |
auditTrail | AuditEntry[] | Ordered log of every node evaluation with timestamps |
{
"transactionId": "tx_9p2kx8f",
"status": "passed",
"stepsExecuted": 6,
"passedNodeIds": ["kyc_check", "sanctions", "aml_threshold", "risk_band", "id_verify", "terminal_pass"],
"failedNodeId": null,
"auditTrail": [
{ "nodeId": "kyc_check", "status": "passed", "timestamp": 1714000001234 },
{ "nodeId": "sanctions", "status": "passed", "timestamp": 1714000001280 }
]
}POST /api/v1/simulate/batch
Run up to 50 payloads against a single ruleset in one call. Recommended for high-volume automated testing and regression suites.
Request body
| Name | Type | Description |
|---|---|---|
rulesetIdrequired | string | The Convex document ID of the target ruleset |
payloadsrequired | object[] | Array of JSON objects to evaluate. Max 50 items. |
testSuiteId | string | Group this entire batch of simulations under a single test suite ID. |
tags | string[] | Apply arbitrary string tags to every simulation in this batch. |
const response = await fetch("https://lumiscompliance.com/api/v1/simulate/batch", {
method: "POST",
headers: {
"Authorization": "Bearer lmk_live_••••••••",
"Content-Type": "application/json"
},
body: JSON.stringify({
rulesetId: "jx7abc123",
payloads: [
{ transaction_amount: 200, user_region: "US" },
{ transaction_amount: 9800, user_region: "EU" }
]
})
});
const result = await response.json();
// Returns: { success: true, data: { transactionIds: ["tx_1", "tx_2"] } }Batch calls consume quota identically to individual calls. A 50-row batch uses 50 simulations from your monthly allowance.
Error codes
| Code | Meaning | Resolution |
|---|---|---|
UNAUTHORIZED | Invalid or inactive API key | Regenerate a key from Settings → API Keys |
QUOTA_EXCEEDED | Monthly simulation limit reached | Upgrade your plan or wait for quota reset |
NOT_FOUND | Ruleset ID does not exist | Verify the rulesetId in your request |
FORBIDDEN | Ruleset belongs to another workspace | Use a rulesetId owned by your workspace |
PAYLOAD_TOO_LARGE | Payload exceeds 64 KB | Reduce payload size or split into multiple calls |
API_KEYS_DISABLED | Trial expired or workspace on Free tier | Upgrade to Pro — check X-Lumis-Reason: trial_expired_keys_paused header |
USAGE_LIMIT_EXCEEDED | Business 100K cap reached, no pay-as-you-go sub active | Enable pay-as-you-go from Settings → Billing — check X-Lumis-Reason: usage_cap_exceeded header |
RATE_LIMITED | Too many requests in a short window | Back off and retry — token bucket refills at 10 req/s |
Rate limits
| Plan | Monthly simulations | Burst rate |
|---|---|---|
| Free | 100 | 10 req / 10 s |
| Pro | 25,000 | 10 req / 10 s |
| Business | 100,000 + Metered | Custom SLA |
Exceeding the free or pro tier limits will result in a 402 Payment Required error. Business usage is metered nightly and invoiced at month-end.
Live Mode (BYOC Engine) API
The Live Mode (BYOC Engine) exposes two surfaces. Control-plane routes run on Lumis infrastructure and speak the same API-key/Bearer contract as the rest of this reference. In-container routes run inside your container on your hosts — live payloads never leave your network.
http://localhost:8080 (deployment-controlled).In-container routes
GET /health
{ "status": "ok", "version": "0.1.0" }GET /pubkey
Returns the engine's Ed25519 public key. Evaluation seals can be verified against this key.
{
"algorithm": "Ed25519",
"public_key": "8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c"
}POST /evaluate
Runs one payload through the cached ruleset bundle. The request body is kept in memory only — never logged or persisted.
ruleset_id string to the request body to select which ruleset runs; omitting it falls back to the workspace default (the most recently activated ruleset). The key is reserved and stripped before evaluation — it will never appear in your rule data or audit trail.curl -X POST http://localhost:8080/evaluate \
-H "Authorization: Bearer lmk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{ "transaction_amount": 1500, "user_region": "US" }'{
"final_status": "passed",
"step_count": 3,
"passed_node_ids": ["start", "pass"],
"failed_node_id": null,
"audit_trail": [
{ "node_id": "start", "action": "check", "result": "passed", "timestamp": 1714000001234 }
],
"cryptographic_seal": "50577f3d718b5d060978854a30fef372e254970247d6d7d8023b05780f3218f861e0ce238a636bc11572bc58bbc429d860105f80b93a44b2c0ae8f5fc73a2d0c"
}Errors: 403 { error: "license_revoked" } (license revoked), 503 { error: "no_ruleset_synced" } (no bundle yet), 404 { error: "unknown_ruleset" } (unrecognized ruleset_id), 404 { error: "multiple_rulesets_require_selector" } (no default and no selector), 400 { error: "…" } (evaluation error).
Control-plane routes
GET /api/v1/engine/sync
Called by the customer's engine to fetch every active ruleset bundle. Requires an active Pro/Business/Enterprise license. Each bundle's signature is an Ed25519 signature over its canonical bundle string; the engine rejects the whole response if any signature fails. default_ruleset_id points at the most recently activated ruleset, used as the fallback when a request does not specify one.
curl -X GET "https://<your-control-plane-url>/api/v1/engine/sync" \ -H "Authorization: Bearer lmk_live_••••••••"
{
"bundles": [
{
"workspace_id": "jf7abc123",
"ruleset_id": "jx7abc123",
"name": "AML Flood",
"region": "US",
"version": 3,
"environment": "active",
"starting_node_id": "start",
"strictness": "medium",
"required_fields": ["user.id", "amount"],
"nodes": [
{ "id": "start", "node_type": "condition", "data": { "label": "Amount check", "field_to_check": "amount", "operator": "gte", "target_value": 10000, "mock_response": null, "audit_code": "AML-01" }, "on_pass": "pass", "on_fail": "fail" }
],
"signature": "50577f3d718b5d060978854a30fef372e254970247d6d7d8023b05780f3218f8…"
},
{
"workspace_id": "jf7abc123",
"ruleset_id": "jx7def456",
"name": "EU GDPR Live",
"region": "EU",
"version": 1,
"environment": "active",
"starting_node_id": "start",
"strictness": "low",
"required_fields": [],
"nodes": [],
"signature": "1a2b3c…"
}
],
"default_ruleset_id": "jx7abc123"
}Errors: 401 { error: "license_not_active" } (Free tier or cancelled/expired subscription). With no active rulesets the response is 200 with "bundles": [] and "default_ruleset_id": null — the engine then clears its cache and /evaluate returns 503 no_ruleset_synced.
POST /api/v1/engine/heartbeat
Counts-only telemetry. The body carries just two numbers — no rule names, no payload fragments, no stack traces. The count is applied to your monthly simulation quota.
curl -X POST "https://<your-control-plane-url>/api/v1/engine/heartbeat" \
-H "Authorization: Bearer lmk_live_••••••••" \
-H "Content-Type: application/json" \
-d '{ "evaluations_count": 17, "period_seconds": 300 }'{ "ok": true }evaluations_count must be an integer in [0, 1000000] and period_seconds in (0, 86400]. Violations return 400 { error: "invalid_telemetry" }. Non-active licenses return 401 { error: "license_not_active" }.
POST /api/v1/engine/export
POST /api/v1/engine/export
Flushes PII-safe evaluation seal records to the workspace's configured SIEM webhook. Enterprise only. Requires a Bearer engine API key. Each record contains the ruleset id/name/version, final status, step count, a sanitized per-node audit trail (node_id, audit_code, result — never payload values), and the Ed25519 cryptographic_seal.
Errors: 400 { error: "invalid_batch" } (oversized or malformed batch), 401 { error: "license_not_active" } (Free/Pro/Business or cancelled/expired).