---
title: "Liveness & PAD policy"
description: "What presentation-attack detection (PAD) is, why it is advisory, why liveness and selfie read at best review, every PAD and liveness reason code, and what to automate and what not."
canonical_url: "https://noxtica.com/docs/identity-verification-liveness-pad-policy/"
markdown_url: "https://noxtica.com/docs/identity-verification-liveness-pad-policy/index.md"
last_updated: 2026-11-05
published: 2026-11-05
section: "identity-verification"
section_title: "Identity Verification"
site: "Noxtica"
language: "en"
source: markdown
---

# Liveness & PAD policy

This page is for a backend engineer or a risk owner who reads the results of a hosted identity verification and wants to know why `checks.liveness` and `checks.selfie` read `review`, what each reason code means, and which decisions your system should leave to a reviewer. It explains what the results mean and what to do with them. Model and vendor names and the internal score limits are not published.

## What it is

**Availability:** Controlled availability. Identity verification is provisioned per tenant by your account team. This page describes what the hosted verification reports for the liveness step and for the selfie; it applies to every hosted verification.

### In short

- **Presentation-attack detection (PAD) is advisory.** Noxtica reports its result and never lets it decide. Every PAD result reads `review`, whatever its score.
- **`checks.liveness` and `checks.selfie` read at best `review`.** `pass` is not reachable for either while PAD is advisory. `scorecard.overall` is therefore at best `review` as well.
- **`review` from `pad_unvalidated_model` is policy, not a capture problem.** The person did nothing wrong, and a retake reads the same.
- **The PAD score is information.** It is not a live or spoof verdict.
- **Two independent layers keep a verification in review:** the check cap above, and the hosted outcome: the automated run of a hosted verification ends `unverified` with `provider_manual_review`, and a reviewer's decision is what moves it on.
- **Nothing here is a tenant setting.** Every limit and both layers are the same for every customer.

### What PAD is

Presentation-attack detection tries to tell a live face in front of a camera from a presentation of a face: a printed photo, a photo or video replayed on a screen, a mask. In hosted verification it looks at frames of the liveness challenge, and separately at the selfie.

PAD is not the same as the gesture checks. The gesture checks (`turn_left`, `turn_right`, `turn_up`, `turn_down`, `blink`) show that the person moved as asked. PAD looks at the pixels of a frame and produces a live score.

### Why PAD is advisory

A PAD score is worth acting on once the point at which a frame counts as live or as an attack has been measured on the population it will judge: genuine people on real devices in real light, and real attacks of each kind. That qualification has not been done on Noxtica's own capture population. Until it is, a high score is not evidence that the person is live and a low score is not evidence of an attack, so the result is reported and never used to decide.

Promoting PAD to a decision needs a new qualification and a documented contract change. When that happens it is published in the API reference and its change log, and this page changes with it. No version is promised here.

### What advisory means for each check

| Surface | What you read while PAD is advisory |
| --- | --- |
| `checks.liveness` (identity read) | `review`, `fail` or `null`. Never `pass`. `fail` when a gesture confidently failed. `null` when no automated result was recorded. |
| `checks.selfie` (identity read) | At best `review`. It reads `fail` only when a hard selfie-quality check fails. |
| `session.scorecard.liveness` | `review` or `fail` (or `pending` / `skipped`). Gesture checks can read `pass`, the `pad` check and the overall `active_liveness` check do not. |
| `session.scorecard.selfie` | At best `review`, with `presentation_attack_detection_inconclusive` and `pad_unvalidated_model` when the quality checks all pass. |
| `session.stages.active_liveness` | `review` or `fail`. `score` is `null`. |
| `session.scorecard.overall` | At best `review`. |
| numeric scores on the `pad` (liveness) and `live_score_band` / `pad_class_score` (selfie) checks | Information for a reviewer and for your own analysis. Not a verdict. Do not set your own live or spoof cut-off on them. |

### The two review layers

| Layer | What it is | What lifts it |
| --- | --- | --- |
| 1. Check cap | `checks.liveness` and `checks.selfie` cannot read `pass` while PAD is advisory. | A PAD whose operating point is qualified and that Noxtica then lets decide. |
| 2. Hosted outcome | The automated run of every hosted verification ends `verification.status: "unverified"` (`verification.source: "pipeline"`) with `reasonCodes: ["provider_manual_review"]` and the session waits in `review_pending`. A reviewer's `approve` makes `verification.status` `verified` (`verification.source: "review"`); an `override` is recorded in `review` and does not upgrade the status. Either one sends `kyc.review.decided`. | A reviewer decision. It is a separate policy from PAD. |

A validated PAD would remove layer 1 only. Layer 2 stays: no check result, however good, makes a hosted verification `verified` by itself.

### PAD reason codes

| Code | Meaning | Status it causes | Emitted today |
| --- | --- | --- | --- |
| `pad_unvalidated_model` | The PAD result is advisory by design. | `pad` check `review` | Yes, on every PAD result |
| `presentation_attack_detection_inconclusive` | Scorecard form of the same fact: the PAD analysis as a whole is `inconclusive`. It appears next to `pad_unvalidated_model` on `scorecard.liveness` and `scorecard.selfie`. | step `review` | Yes |
| `pad_face_not_detected` | The PAD analysis did not find a face on a frame, even though other checks may have. The verification does not end in an error: the other checks continue. In the liveness result it comes with `pad_unvalidated_model`. | `review` | Yes |
| `pad_unavailable` | PAD could not run. | `review` | No, reserved. A liveness check that cannot run at all shows one of the "could not run" codes below instead. |
| `pad_incomplete` | PAD did not evaluate every challenge. | `review` | No, reserved |
| `pad_review` | A PAD score that falls in the middle band once PAD decides. | `review` | No, reserved for when PAD decides |
| `pad_rejected` | A PAD score that falls in the lowest band once PAD decides. | `fail` | No, reserved for when PAD decides |

Reserved codes are part of the published vocabulary so that your code can already accept them. Do not branch on them today: they are not produced.

### Gesture reason codes

These explain a gesture check that did not pass. The gesture checks are `turn_left`, `turn_right`, `turn_up`, `turn_down` and `blink` (the passive variant has one `passive_hold` check). A check that reads `review` is written `<name>_inconclusive` in `scorecard` reason codes, and a failed one `<name>_failed`.

| Code | Meaning | Status it causes |
| --- | --- | --- |
| `challenge_direction_inconsistent` | The head movement did not match the requested direction. It is present when the gesture failed, or when the gesture reads `review` and no more specific cause below applies. A `review` that names its cause (for example `client_metrics_unavailable`) does not carry it. | gesture `fail` (or `review`) |
| `landmark_peak_mismatch` | The browser's reported peak frame does not match the server's measurement. For up and down it is judged just when the head-pose measurement was available. | gesture `fail` (turn left and right, and up and down with a head-pose measurement) |
| `no_face_detected`, `multiple_faces` | A frame had no face, or more than one face. | gesture `fail` |
| `face_track_lost` | The face was lost between frames. | `review` or `fail` |
| `client_metrics_unavailable`, `client_landmark_low_confidence` | The browser did not provide its own measurements, or provided them with low confidence. A blink is judged by the server on its own frames and does not carry them. | caps at `review` |
| `neutral_reference_unavailable` | No usable starting pose (fewer than three single-face frames in the blink burst). | `review`, never `pass` |
| `pose_landmarks_unavailable` | The head-pose measurement was unavailable for a frame, so up and down cannot be judged. | pitch gesture `review` |
| `blink_inconsistent` | The blink could not be confirmed. When a blink-like cycle existed it comes with one of the three codes below. | blink `review` or `fail` |
| `blink_closed_plateau` | A real eye closing and reopening was found, but the closed frame sits next to another almost identical near-closed frame. | blink `review` |
| `blink_scene_changed` | The blink was found, but the nose region, which serves as a control for lighting and head movement, changed between the open frame and the reopened frame. | blink `review` |
| `blink_cycle_partial` | Just part of the evidence for a blink was present, or the cycle was incomplete. | blink `review` |
| `blink_duration_out_of_range` | The blink cycle lasted outside 50 to 1500 ms. | blink `review` |
| `passive_temporal_evidence_unavailable`, `passive_temporal_inconsistent`, `motion_implausible`, `frame_duplicate_suspected` | Passive-hold variant: not enough or implausible motion across the three frames, or repeated frames. `passive_temporal_inconsistent` is present when the result is `fail`, or when a `review` has no more specific cause. | `review` (inconsistent: `fail`) |

The scorecard also carries `active_liveness_inconclusive`, `active_liveness_failed` and `active_liveness_fail` for the overall liveness result.

### Codes for a liveness check that could not run

When the liveness check could not run at all, the result is `review`, never `fail`, and the stage reason is one of these codes. They say that no gesture was judged, not that the person failed.

| Codes | Meaning |
| --- | --- |
| `evidence_missing`, `evidence_unavailable`, `evidence_wrong_content_type`, `invalid_evidence_package`, `evidence_lookup_failed` | The capture package was missing, could not be read, was the wrong kind of file, was not a valid package, or could not be looked up. |
| `transport_error`, `unexpected_status`, `malformed_response`, `malformed_refusal_body` | The verification service did not answer in time, refused the request, or answered in a form that could not be read. |
| `adapter_not_configured`, `adapter_config_invalid`, `not_configured`, `weights_ungated` | The liveness analysis was not set up for the environment or was not available there. |

## Prerequisites

- A hosted verification that has reached the submit step, so that a result exists. Before that, the liveness step reads `pending`.
- A server key that can read the session status (`GET /v1/kyc/sessions/{sessionId}`, see [Server API reference: KYC](https://noxtica.com/docs/api/reference/kyc/)), and, for `checks.*`, the identity read (`GET /v1/kyc/verifications/{verificationId}/identity`, permission `read:identity_details`).
- A webhook endpoint subscribed to `kyc.review.decided` (and `kyc.session.completed` for a hosted session), if you complete your own flow on the reviewer's decision.

## Steps

### Decide what your system does with a result

1. **Take the outcome from the reviewer decision.** Read `verification.status`, `verification.source` and `review` in the session status, or react to `kyc.review.decided`. Treat `unverified` / `pipeline` with `provider_manual_review` as "waiting for a person".
2. **Do not auto-approve on any check.** No check result changes `verification.status`, and no check, `pass` included, replaces the reviewer decision.
3. **Do not read a gesture `pass` as proof that the face was live.** The gesture checks show that the requested movement happened. They are not a presentation-attack test.
4. **Do not treat `review` with `pad_unvalidated_model` as a capture failure.** Do not ask the person for a new capture because of it.
5. **Read `fail` as "a gesture confidently failed".** Use the gesture check and its reason code to see which one. A `fail` on a person who is clearly genuine is a false reject: report it with the `sessionId`.
6. **Treat the PAD score as information.** Show it to your own reviewers if you like. Do not turn it into a cut-off.
7. **Do not auto-reject on `review`.** `review` means "no confident answer, a person should look".

### Read per-gesture checks as information

In `session.scorecard.liveness.checks`, read the five gesture checks first. When every gesture check is `pass: true` and only `active_liveness` and `pad` are `pass: false`, the person did all five gestures and the `review` is the PAD policy. When a gesture check is `pass: false`, its reason codes say why.

## Verify it works

Take a hosted session whose person completed the liveness step and the selfie, and read `GET /v1/kyc/sessions/{sessionId}`. A result that follows this policy looks like this (the values are an illustration):

| Where | Value |
| --- | --- |
| `session.verification` | `status: "unverified"`, `source: "pipeline"`; `reasonCodes` holds `provider_manual_review` while the session waits in `review_pending` |
| `session.scorecard.liveness.status` | `review` |
| `session.scorecard.liveness.checks` | `turn_left`, `turn_right`, `turn_up`, `turn_down`, `blink`: `pass: true`. `active_liveness`, `pad`: `pass: false`. |
| `session.scorecard.liveness.reasonCodes` | `active_liveness_inconclusive`, `presentation_attack_detection_inconclusive`, `pad_unvalidated_model` |
| `session.scorecard.selfie.status` | `review`, with `presentation_attack_detection_inconclusive` and `pad_unvalidated_model` |
| `checks.liveness`, `checks.selfie` (identity read) | `review`, `review` |
| after a reviewer approves | `session.verification` reads `status: "verified"`, `source: "review"`, `session.review.decision` is `approve`, `kyc.review.decided` is delivered, and `provider_manual_review` stays in `reasonCodes`. The check results do not change. |

If your system reads this as a problem with the person's capture, change the rule in your system: this is the expected reading.

## Troubleshooting

| You see | It means | What to do |
| --- | --- | --- |
| `checks.liveness: "review"`, every gesture `pass: true` | PAD policy (layer 1). | Nothing. Wait for the reviewer decision. |
| `checks.selfie: "review"`, quality checks all `pass` | PAD policy (layer 1). | Nothing. Do not ask for a new selfie. |
| `verification.status: "unverified"`, `provider_manual_review`, `review: null` | Layer 2. A reviewer has not decided yet. | Wait for `kyc.review.decided`. |
| `pad_face_not_detected` | The PAD analysis found no face on a frame. The rest of the verification continued. | Nothing in your integration. A reviewer looks at it. |
| `blink_inconclusive` | The blink was not measured or not confirmed. | Treat it as "not measured", not "no blink". A reviewer looks at it. |
| `turn_*_failed` | The head barely moved, the face was very small, or the light was poor. | Your policy: the hosted page already has a retake budget for the person. |
| `fail` on a genuine person | A false reject. | Report it with the `sessionId`. |
| A stage reason such as `evidence_missing` | The liveness check could not run. | A reviewer looks at it. It is not a failure of the person. |

## Related

- [Identity verification integration](https://noxtica.com/docs/identity-verification-integration/): create a session and read the outcome.
- [Read identity details with the API](https://noxtica.com/docs/identity-verification-identity-details/): the `checks.*` summary of a verification.
- [Identity verification events and webhooks](https://noxtica.com/docs/identity-verification-webhooks/): `kyc.review.decided` and `kyc.session.completed`.
- [Identity verification FAQ and troubleshooting](https://noxtica.com/docs/identity-verification-faq/): statuses, review and a five-step triage.
- [Server API reference: KYC](https://noxtica.com/docs/api/reference/kyc/): every check, threshold and reason code of the other steps, under "Check results" on `GET /v1/kyc/sessions/{sessionId}`.
