Docs
Liveness

Liveness & Face Match

Check that a live person is in front of the camera and, optionally, that they match a reference photo

Status: prototype. The liveness module is an anti-fraud layer. It raises the cost of replay, injection and synthetic-media attacks; it is not a certified presentation attack detection product, and a pass verdict is evidence to combine with your other onboarding measures, not proof on its own. What it is designed to raise the cost of, and what it does not cover, is in the threat model.

The module records a short capture (a video plus a few still frames) from the subject's camera and analyses it server-side. Nothing the client reports is trusted: every liveness fact is re-derived from the uploaded media. An active capture takes about a minute of the subject's time, a passive one a few seconds.

Check familyWhat it verifies
ChallengeThat the subject followed a script of timed prompts issued by the server for this session, re-verified from the uploaded frames and from the video. Every active profile runs the same script family, on a laptop webcam as on a phone
TemporalOne face per frame, one identity across the whole capture, frames that belong to the video, media not seen before
ForensicsA third-party forensic analysis of the video, of selected frames and of the reference photo
Face matchOptional 1:1 comparison between a reference photo and the most frontal frame of the capture

The result is a verdict (pass, review or fail), a risk score from 0 to 100 and a per-check report.

Two ways to integrate

ModeYou provideThe platform providesKey
Hosted flowA session, and the capture link handed to the subjectThe capture page (consent, camera, timed prompts, upload), the challenge protocol, the analysis, the report, the return to your page and the completion webhookPublishable or secret
API-onlyThe media (a video and/or frames, optionally a reference photo) posted from your backendThe analysis and the report; no challenge, since the platform did not drive the captureSecret

The hosted flow is the one with challenge verification. API-only analysis runs the passive checks alone (forensics, temporal consistency, face match) and is meant for media captured by your own application.

Hosted flow

  1. Create a session with POST /liveness/sessions, choosing a profile. The response carries sessionId, a one-time sessionToken, the captureUrl and expiresIn: 600 (10 minutes to start the capture).
  2. Hand the capture link to the subject. Show captureUrl as a QR code for a phone handoff, or open it on the same device. The session token travels only in the URL fragment (#s=<sessionId>.<sessionToken>), so it never reaches server logs or a Referer header. Do not copy it into a query string.
  3. Optionally upload a reference photo (POST /liveness/sessions/:id/reference with your API key, or from the capture page with the session token) before the capture. Profiles whose reference.required is true reject a capture without one.
  4. The subject captures. The hosted page collects consent, records the camera through the prompts and uploads the media in one request. Allow about a minute for an active profile. A session accepts one capture at a time, and up to three in all: when the platform cannot measure the subject's images reliably, it asks the subject to do it again on a fresh challenge instead of deciding on a weaker measurement (see retakes).
  5. Get told the session is over. Configure a completion webhook once and the platform posts the session id, status, verdict and risk score as soon as the session is terminal, signed and retried. Polling GET /liveness/sessions/:id every couple of seconds is still supported and still the authoritative read; while processing, the document carries progress: { stage, percent }.
  6. Bring the subject back. Pass a returnUrl when you create the session and the capture page offers the subject a way back to your page when they are done. It carries the session id and nothing else: the verdict is read by your back end, never handed to the browser.
  7. Act on verdict, riskScore and, for review cases, the checks list.

Steps 5 and 6 are the two halves of the handover, and they are not interchangeable: the webhook is how your system learns the outcome, the return URL is how the subject gets back to your flow. The browser never carries the result.

The capture page is ours, and the challenge protocol it runs is not part of the public contract. Media captured by your own application goes through API-only analysis instead, which carries no challenge checks.

Session lifecycle

StatusMeaning
createdSession exists, capture not started
challengedThe capture page has taken the session in hand
processingThe capture was accepted and the pipeline is running
retakeThe capture could not be measured reliably and nothing failed: the subject is asked to capture again, on a fresh challenge. Not a verdict
completedThe report is available
errorThe pipeline failed (failureReason says why)
expiredNo capture arrived within 10 minutes of creation, or of the last retake

completed, error and expired are terminal, and each fires the completion webhook once if one is configured. retake is not: it moves back to challenged when the subject starts again, fires no webhook and carries no verdict. A session allows 3 capture attempts; a third attempt that cannot be measured either completes the session with verdict: "fail" and failureReason: "images_unmeasurable". So a retake is never mistaken for a failure: a failure is always completed with verdict: "fail". expired is written by a sweep that runs every 5 minutes (the same sweep times out a pipeline that overran its budget), so its webhook lands up to 5 minutes after the deadline; GET /liveness/sessions/:id reports expired as soon as the deadline has passed.

API-only sessions created through POST /liveness/analyze go straight to processing and come back completed in the same response.

Attested capture

The hosted page is a browser: the platform verifies what it uploads, and the threat model says what a browser cannot promise about where the bytes came from. The mobile-attested profile runs the capture in the Verifiables mobile application instead. The app records, digests the exact bytes it is about to upload, and signs those digests together with a single-use nonce the platform issued for this session, using the device's hardware-backed app attestation. The platform checks the digests against what it received, verifies the signature, and reports the outcome as the attestation.device check, which is required on that profile.

What it proves: that this application, unmodified, on an intact device, produced these bytes after this nonce. What it does not prove: what the camera saw. It is a provenance fact about the upload, to be read together with the other checks, not a certified presentation attack detection, a proof of presence or an identity-proofing path on its own. iOS is verified today; an Android capture on this profile fails the required check with platform_unsupported, never a pass and never a silent skip. The sessions are created, handed over and read exactly like any other, with a capture link the app recognises: see the subject endpoints for the two additions to the capture contract.

Degraded modes

The pipeline never aborts because a provider is missing: the affected checks get the status unavailable, and the profile's unavailablePolicy decides what that does to the verdict (review, fail or ignore). This includes the head-angle checks of an active profile when the face landmark model is not available on our side (reason mesh_engine_unavailable): an outage of ours is reported as such, never turned into a retake the subject cannot fix. GET /liveness/status reports which providers are ready, so you can surface a degraded state in your own health checks.

Data retention

  • The consent version and acceptance timestamp sent by the capture page are stored with the session and echoed in the report (client.consent).
  • Media artifacts (video, frames, reference photo, forensic heatmaps) are kept for retention.artifactsHours after the capture, 24 hours by default and configurable per profile. A purge job then drops their bytes; the report keeps their ids, hashes and sizes with purged: true, and the artifact endpoint answers 410.
  • The report JSON contains no binaries and is not purged by the artifact job.
  • The temporal.media_reuse check compares the upload against your organisation's retained artifacts, so its reach is bounded by the retention window: a short retention.artifactsHours shortens the memory of that check.
  • Face embeddings and decoded pixels never leave process memory. Session tokens are stored as a hash only. Media sent to the forensic stage is deleted on its side once the analysis returns.
  • Raw capture media (video, video_mp4, reference artifacts) is served to secret keys only; frames and heatmaps are served to publishable keys as well.