Overview
Warnings on the liveness report flag every condition Didit observed while running the liveness check. They land in thewarnings[] array on each item of liveness_checks[] (see Liveness report), with feature set to "LIVENESS". Each entry follows the shared warning object shape: feature, risk, additional_data, log_type, short_description, long_description, node_id.
Every warning has three layers:
- The
riskcode — a stable identifier you can match on in your code (the codes listed below). - The
log_type— one ofinformation,warning, orerror. Auto-decline risks always carryerror. For configurable risks it is derived from the configured action: No action →information, Review →warning, Decline →error. For threshold-driven risks (LOW_LIVENESS_SCORE,LOW_FACE_QUALITY) it iserrorwhen the value crosses the decline threshold andwarningwhen it only crosses the review threshold. - The decision impact — the same routing drives the liveness report’s
status: a Review action moves it toIn Review, a Decline action (or any auto-decline risk) toDeclined, and No action leaves it untouched.
PASSIVE (multiple-faces, quality, and luminance checks). The tables below list the full set of liveness risk codes and their severities — no other liveness risk codes exist.
Auto-decline conditions
The following risks always force the liveness report (and the session) toDeclined, with log_type: "error":
When
NO_FACE_DETECTED fires, LOW_LIVENESS_SCORE is suppressed (there is no face to score).
Configurable verification settings
In the Didit console, liveness risks are grouped under the following workflow settings:Warnings produced
Core liveness
Capture channel integrity
Injection-attack signals reported by the client at capture time. Unlike presentation attacks (an artefact held in front of a real camera), these describe the camera feed itself being replaced or intercepted. Each is configurable per workflow and defaults to no action, so enabling the feature changes no decisions until you opt in.Browsers expose no OS-level camera-source attestation to any vendor, so on web these signals are corroborating evidence rather than proof. On web, a verdict of
null means not determined (device labels are unavailable until camera permission is granted) and never fires a risk.FRAME_INJECTION_SUSPECTED instead: it is detected from the device environment - the rendering stack and the device model reported in the user agent - rather than the camera name. Findings raised from the device environment carry additional_data.reason: "emulated_capture_device", distinguishing them from the liveness model’s own frame analysis.
Detecting the machine from a browser is evidence, not attestation, and a determined attacker can interfere with it. The native mobile SDKs report stronger, operating-system-level integrity signals a browser cannot see (emulator, rooted or jailbroken device, runtime hooking, attached debugger), each with its own configurable action under Device and runtime integrity. These signals raise the attacker’s cost considerably, but on their own they do not prove that a capture came from a genuine device. Device integrity signals are independent of the liveness method, so they combine with any of Passive, 3D Flash or 3D Action & Flash.
Recording sanity forensics
LIVENESS_VIDEO_ANOMALY is computed server-side on the stored liveness recording, independently of the liveness model. It detects a moving sequence repeated across multiple cycles, consistent with a looped feed.
Set the action on your Liveness or Age Estimation node:
NO_ACTION records an information finding, REVIEW can escalate a decided verification to In Review, and DECLINE can escalate it to Declined. The check runs asynchronously, so an escalation can arrive after the initial result. A more severe existing decision is not downgraded, and manual review decisions are preserved.
Version 2 findings include forensics_version: 2, repeated_sequence evidence and repeated_moving_sequence in anomaly_reasons. Delivery measurements such as unique_frame_ratio, static_pair_ratio, decoded_fps_ratio and max_frame_gap_seconds remain diagnostics; they cannot establish a replay on their own. Older findings describe the detector version used at the time and are not retroactively reclassified.
This is a conservative additional signal, not complete detection of synthetic media or injected video. A recording with no anomaly is not proof of liveness. Short or ambiguous captures and captures without usable replay evidence remain unflagged by this check. Missing or unreadable recordings produce no finding from this check; other verification checks still apply.
Cross-session face matching
For matches against imported faces or list-entry faces (which have no session), the
*_session_id, *_session_number, and api_service values in additional_data are null.
Faces belonging to the same user (same vendor_data, or the same vendor user when available) are excluded from the entire index search — both duplicate detection and blocklist screening. Match precedence is: blocklist exact → allowlist exact → duplicate exact → blocklist possible → allowlist possible → duplicate possible — only one of these six mutually exclusive codes appears per report. DUPLICATED_FACE_NAME_MISMATCH is not part of that precedence group: it is an additional code emitted on top of DUPLICATED_FACE when the identity check also fails, never in its place. additional_data.name_match_score never carries the names being compared — only ids and a score, so no PII lands in the warning payload.
Passive liveness quality (only when method = "PASSIVE")
LOW_FACE_LUMINANCE and HIGH_FACE_LUMINANCE are mutually exclusive — the luminance is either below the minimum or above the maximum, never both.
Age estimation (adaptive workflows and AGE_ESTIMATION nodes)
Exact description strings
The API returns these exactshort_description and long_description strings for each risk:
Standalone passive liveness API
The standalone Liveness API reuses the same risk codes with a few behavioral differences:NO_FACE_DETECTEDandLOW_LIVENESS_SCOREare mutually exclusive — the score check only runs when a face was found.LOW_LIVENESS_SCOREfires against the request’sface_liveness_score_decline_threshold(default 30) and always carrieslog_type: "error".POSSIBLE_FACE_IN_BLOCKLISTis treated as a decline (log_type: "error") instead of review.MULTIPLE_FACES_DETECTEDcarrieslog_type: "warning", while the duplicate-face risks stay informational. Onlyerror-level warnings flip the standalone response’sstatustoDeclined.- Standalone warning entries include
featurebut nonode_id.
Examples
Auto-decline on detected attack
Configurable warnings on a passive capture
Duplicate face with payload
log_type: "warning" and the liveness report’s status is In Review. With the default (No action) it would carry log_type: "information" instead. api_service is null because the matched face came from a workflow session, not a standalone API call.
Duplicate face under a different name
DUPLICATED_FACE stays at its own configured action (here, No action — log_type: "information") while DUPLICATED_FACE_NAME_MISMATCH carries its own action, defaulting to Review (log_type: "warning"). The liveness report’s status follows the most severe of the two: In Review here, even though the plain duplicate-face setting alone would not have flagged it.
Recording sanity anomaly with its measurements
log_type is information because the configured action is NO_ACTION. The frame-delivery diagnostics, omitted from this example for brevity, do not trigger the finding.
Warning types
Each risk is assigned a severity based on your application’s configuration. The three severities are:Related
- Liveness report — full report schema and statuses.
- Face match warnings — companion 1:1 face match warnings.
- Webhooks —
session.status.updatedcarries the warnings as soon as the liveness step finishes. - Data models — Liveness check — canonical schema with every field.
- Data models — Warning object — the shape of every entry in
warnings[].