Documentation
Everything you need to go from an empty page to a live behavioral verdict. The Behavioral Security product (Lava Sense) is generally documented here; Language Intelligence and Share Without Sharing are in early access — partnerships@cleanlava.com for those.
Overview
Lava Sense has two halves:
- A browser SDK (
lava-sense.js, ~4 KB) that measures how a person types and moves, reduces that behavior in the browser to a small set of timing statistics — about a dozen session summaries plus up to 64 per-key timing means under opaque, device-salted labels — and discards the raw events. - A REST API that enrolls those numbers into a per-device baseline over the first few sessions, then scores every subsequent session against it — returning a
risk_scoreand a verdict ofALLOW,STEP_UP, orBLOCK, with differential-privacy noise applied to every score.
The API also serves an interactive OpenAPI explorer at /docs on the API host, with no key required — every request and response schema on this page can be exercised there.
X-API-Key are issued when you're onboarded as a design partner. The live demo runs against our hosted deployment if you want to see the engine work first.
Quickstart
First verdict in under five minutes. Include the SDK and point it at your key:
<script src="https://cleanlava.com/demo/lava-sense.js"></script>
// 1. Create a sensor and attach it to the surfaces you care about
const sense = LavaSense.create({
apiBase: 'https://YOUR-API-BASE',
apiKey: 'YOUR-API-KEY',
appId: 'my-banking-app',
});
sense.attachKeyboard(document.querySelector('#login-form'));
sense.attachPointer(document);
// 2. Start a session when the user arrives
const session = await sense.startSession();
// 3. Before anything high-value: submit features, get the verdict
await sense.submitFeatures(session.session_id);
const verdict = await sense.getVerdict(session.session_id);
if (verdict.verdict === 'STEP_UP') {
// ask for an OTP — this session doesn't feel like the account owner
}
The first few sessions per device return mode: "enrolling" while the
baseline is learned (verdict is ALLOW during enrollment). After that,
every session is scored live.
SDK reference — LavaSense.create(options)
Creates a sensor instance. All raw capture buffers live inside this instance and never leave the page.
| Option | Type | Description |
|---|---|---|
apiBase | string | Base URL of the Clean Lava API. |
apiKey | string | Your API key; sent as the X-API-Key header. |
appId | string | An identifier for your application; baselines are scoped to it. |
localStorage, and sends a SHA-256 hash of it. There is no fingerprinting and nothing tied to the person's identity.
Capturing behavior
| Method | What it measures |
|---|---|
sense.attachKeyboard(el) | Typing rhythm on el: key dwell times, flight times between keys, correction rate. Key values are never read (only Backspace/Delete are counted as corrections). |
sense.attachPointer(el) | Mouse and touch style on el (usually document): movement velocity and curvature signatures, click dwell. |
Sessions & verdicts
| Method | Returns | Description |
|---|---|---|
sense.startSession() | { session_id, ... } | Opens a scoring session for this device and app. |
sense.submitFeatures(id) | ack | Reduces the captured behavior to timing statistics (session summaries + opaque per-key means) and submits them. Raw buffers are cleared. |
sense.getVerdict(id) | verdict object | Scores the session and returns the risk verdict. |
REST API — authentication & limits
Every endpoint except /v1/health requires your key:
X-API-Key: your-api-key-here
- All requests and responses are JSON.
- Requests are rate-limited per key; sustained overage returns
429. - CORS is restricted to registered partner origins in production.
Endpoints
Liveness check. No API key required.
Opens a session. Send the hashed device_id, your app_id, and optional device context. Returns a session_id.
Optional lightweight heartbeat of event counts during a session (used by the simulated fallback). Carries no behavioral content.
Submits the on-device feature summary (session timing statistics plus up to 64 per-key timing means under opaque labels). This is the only behavioral payload the API accepts — there is no endpoint that accepts raw events.
Scores the session and returns the verdict object. Repeated calls on the same session are idempotent — a session is never enrolled twice.
Erases the enrolled behavioral baseline for a device — the right-to-erasure call. The next sessions re-enroll from scratch.
The verdict object
{
"session_id": "3f6a…",
"mode": "live", // "enrolling" | "live" | "simulated"
"risk_score": 0.08, // 0.0 no risk → 1.0 high risk (DP noise applied)
"verdict": "ALLOW", // "ALLOW" | "STEP_UP" | "BLOCK"
"signals": {
"typing_rhythm_match": 0.94,
"pointer_signature_match": 0.91,
"device_context_match": 0.99,
"sim_status": "unknown" // real SIM signal requires the mobile SDK
},
"enrollment": {
"status": "enrolled",
"sessions_recorded": 5,
"sessions_required": 3
},
"processing_time_ms": 12,
"privacy": {
"raw_data_stored": false,
"processing_location": "on-device",
"differential_privacy_applied": true,
"epsilon": 1.0
}
}
Modes
enrolling— the device's baseline is still being learned over its first sessions. Verdict isALLOWand no match scores are returned.live— real statistical scoring against the enrolled baseline. Low-risk sessions continue to refine the baseline so it adapts as behavior drifts.simulated— the session never submitted features, so clearly-labelled simulated scores are returned as an API-exploration fallback.
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid X-API-Key. |
404 | Unknown session_id or device_id. |
429 | Rate limit exceeded — back off and retry. |
5xx | Our fault. Fail open or closed per your risk policy; report persistent errors to support@cleanlava.com. |
Error bodies are JSON: { "error": "Not Found", "message": "…" }.
Privacy model
What we process, what we store, and what we can never see:
| Data | Where it lives | Retention |
|---|---|---|
| Raw keystroke & pointer events | Browser memory only | Discarded at feature reduction; never transmitted |
| Key values (what was typed) | Never read by the SDK | — |
| Feature summaries (timing statistics only) | Clean Lava API | Folded into the device baseline, then discarded |
| Device baseline (statistical profile) | Clean Lava API | Until erased via DELETE /v1/devices/{id}/baseline |
| Scores returned to you | Your systems | Always carry differential-privacy noise |
This architecture is designed against NDPA 2023 (with GAID 2025 guidance) and GDPR: data minimization by construction, explicit consent surfaces in the SDK integration, and right to erasure as a first-class API call. Compliance questions: support@cleanlava.com.