Not every transaction needs the same level of verification. A $50 transfer and a $50,000 withdrawal face different threat profiles and different user expectations. That’s why Lorica offers three liveness modes — each designed for a specific point on the friction-security spectrum.
The mode is selected per-verification via the liveness_mode parameter in the API call. Your platform decides which mode to trigger based on the risk level of the action. The canonical framing: passive · dual_frame · motion.
Match the mode to the risk. A $200 transfer shouldn’t require a head-turning challenge. A $200,000 withdrawal absolutely should.
The three modes
Passive — lowest friction
- ~40ms added latency
- Single frame capture
- Catches: photos, basic deepfakes
The user doesn’t even notice. A single frame is captured from the webcam, analyzed for texture patterns, depth cues, and injection artifacts. No prompts, no instructions, no waiting. The verification happens behind the scenes in the same moment the user clicks “Confirm withdrawal.”
Best for: medium-value transactions where you want verification without any added friction. Transfers between $1,000 and $10,000. Routine account actions. Situations where a failed liveness check would frustrate legitimate users disproportionately to the risk.
{
"user_id": "usr_8f3a2b1c",
"image": "[base64]",
"action_context": "transfer_5000_usd",
"liveness_mode": "passive"
}
Dual-frame — balanced
- ~80ms + 3s capture gap
- Two frames, 3 seconds apart
- Catches: photos, screenshots, frozen feeds, basic deepfakes
Two frames are captured approximately 3 seconds apart. The system compares them for natural movement — micro-shifts in head position, lighting changes, subtle expression variation. If the structural similarity between frames exceeds 0.7, it’s a static image: a screenshot held up to the camera, a frozen video feed, or a printed photo.
Real humans can’t hold perfectly still for 3 seconds. Printed photos can’t change at all. This single comparison catches an entire class of presentation attacks that passive mode might miss.
Best for: high-value transactions. Withdrawals above $10,000. Large trades. Account recovery. API key generation. Situations where the 3-second pause is acceptable because the stakes justify it.
// Step 1: Get a challenge token
GET /challenge?user_id=usr_8f3a2b1c
// Step 2: Capture frame 1, wait 3s, capture frame 2
{
"user_id": "usr_8f3a2b1c",
"image": "[base64_frame1]",
"image2": "[base64_frame2]",
"challenge_token": "[token]",
"action_context": "withdraw_25000_usdc",
"liveness_mode": "dual_frame"
}
Motion — maximum security
- ~120ms + user action time
- Random challenge-response
- Catches: everything — photos, deepfakes, screen replay, injection
The server issues a random challenge: “Turn your head left.” “Blink twice.” “Nod.” The user performs the action in front of the camera. The captured frames are analyzed for compliance with the specific challenge. A pre-recorded video can’t respond to a random prompt it’s never seen.
This is the hardest mode to defeat. An attacker would need a real-time deepfake engine that can interpret natural language commands and generate physically plausible head movements at camera frame rate. That capability exists in research labs, but it’s not in the wild at production quality — yet.
Best for: maximum-risk actions. Withdrawals above $100,000. Whitelisting new withdrawal addresses. Changing account email or phone. Recovery key generation. Any action where the cost of fraud vastly exceeds the cost of user friction.
// Step 1: Get a challenge
GET /challenge?user_id=usr_8f3a2b1c&mode=motion
// Returns: { "challenge": "turn_left", "token": "..." }
// Step 2: User performs challenge, capture frames
{
"user_id": "usr_8f3a2b1c",
"frames": ["[base64_1]", "[base64_2]", "[base64_3]"],
"challenge_token": "[token]",
"action_context": "whitelist_address_0x4f2a",
"liveness_mode": "motion"
}
The decision matrix
| Transaction type | Risk level | Recommended mode | User friction |
|---|---|---|---|
| Transfer $100-$1,000 | Low | passive | Zero — invisible |
| Transfer $1,000-$10,000 | Medium | passive | Zero — invisible |
| Withdrawal $10,000-$50,000 | High | dual_frame | 3-second pause |
| Withdrawal $50,000+ | Critical | motion | 5-10 seconds |
| Whitelist new address | Critical | motion | 5-10 seconds |
| Change email/phone | Critical | motion | 5-10 seconds |
| Generate API keys | High | dual_frame | 3-second pause |
| Account recovery | Critical | motion | 5-10 seconds |
The security tradeoff
There’s an honest tradeoff here. Passive mode is invisible but catchable by a sophisticated attacker with a real-time deepfake. Motion mode is nearly impossible to defeat but adds friction that some users will find annoying. Dual-frame sits in the middle.
The right answer isn’t “always use motion.” The right answer is match the mode to the risk. A $200 transfer shouldn’t require a head-turning challenge. A $200,000 withdrawal absolutely should.
Most exchanges we talk to implement a simple threshold: passive below $10K, dual-frame $10K-$50K, motion above $50K and for all account-level changes. The thresholds are configurable — your risk team sets the boundaries, Lorica enforces them.
All three modes share the same pipeline
Regardless of which liveness mode you select, every verification passes through the same verification pipeline. The mode only changes how liveness is assessed — everything else (face detection, embedding match, replay detection, injection analysis, JWT signing) runs identically. The signed JWT contains a liveness_method field so your auditors can see exactly which mode was used for each transaction.
Try all three modes in the live demo: loricaapi.com/demo