Static Liveness Check Implementation
Overview
This implementation adds support for Static Liveness Check to the Yoti Python SDK, enabling identity verification using a single static image instead of the ZOOM liveness method which requires multiple frames and facemap data.
Features Added
1. Session Creation (Request Side)
Create STATIC Liveness Check
from yoti_python_sdk.doc_scan import RequestedLivenessCheckBuilder
# Build a STATIC liveness check
liveness_check = (
RequestedLivenessCheckBuilder()
.for_static_liveness()
.with_max_retries(3)
.with_manual_check_never()
.build()
)Generated JSON:
{
"type": "LIVENESS",
"config": {
"liveness_type": "STATIC",
"manual_check": "NEVER",
"max_retries": 3
}
}2. Session Retrieval (Response Side)
Access STATIC Liveness Resources
from yoti_python_sdk.doc_scan import DocScanClient
client = DocScanClient(sdk_id, key_file_path)
session_result = client.get_session(session_id)
# Get all STATIC liveness resources
static_resources = session_result.resources.static_liveness_resources
for resource in static_resources:
print(f"Resource ID: {resource.id}")
print(f"Liveness Type: {resource.liveness_type}")
# Access the image and media
if resource.image and resource.image.media:
media_id = resource.image.media.id
media_type = resource.image.media.type
created = resource.image.media.created
print(f"Media ID: {media_id}")
print(f"Media Type: {media_type}")3. Media Content Retrieval
Download STATIC Liveness Image
# Get the first STATIC liveness resource
static_liveness = session_result.resources.static_liveness_resources[0]
# Extract media ID
media_id = static_liveness.image.media.id
# Retrieve the actual image content
media_content = client.get_media_content(session_id, media_id)
# Access the image bytes and MIME type
image_bytes = media_content.content
mime_type = media_content.mime_type # e.g., "image/jpeg" or "image/png"
# Save to file
with open(f"liveness_image.{mime_type.split('/')[-1]}", "wb") as f:
f.write(image_bytes)Run Tests
# Run STATIC liveness tests only
pytest yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py -v
pytest yoti_python_sdk/tests/doc_scan/session/retrieve/test_static_liveness_resource.py -v
# Run all doc_scan tests
pytest yoti_python_sdk/tests/doc_scan/ -vExample Application
The Flask example application (examples/doc_scan/) now displays Static Liveness resources on the success page:
- Shows resource ID and liveness type
- Displays the static liveness image
- Provides media ID for reference
- Uses collapsible accordion UI similar to ZOOM liveness
Backward Compatibility
✅ Fully backward compatible - All existing code using ZOOM liveness continues to work without any changes:
# Existing ZOOM liveness code still works
zoom_check = RequestedLivenessCheckBuilder().for_zoom_liveness().build()
zoom_resources = session_result.resources.zoom_liveness_resources
# New STATIC liveness code
static_check = RequestedLivenessCheckBuilder().for_static_liveness().build()
static_resources = session_result.resources.static_liveness_resourcesAcceptance Criteria
All three acceptance criteria have been met:
-
✅ Add support for requesting a liveness check type STATIC
- Implemented via
for_static_liveness()builder method - Supports
manual_checkparameter (defaults to"NEVER")
- Implemented via
-
✅ Add support for retrieving the updated liveness check response
- Created
StaticLivenessResourceResponseclass - Added
static_liveness_resourcesfilter property - Parses image and media objects correctly
- Created
-
✅ Ensure that the SDKs support retrieving the media for the STATIC liveness check
- Media ID accessible via
resource.image.media.id - Existing
get_media_content()method works seamlessly - Example application displays the image
- Media ID accessible via
For Existing Implementations
No changes required! Your existing ZOOM liveness code will continue to work. You can optionally add STATIC liveness support:
# Add STATIC liveness alongside existing ZOOM liveness
session_spec = (
SessionSpecBuilder()
.with_requested_check(
RequestedLivenessCheckBuilder()
.for_zoom_liveness() # Existing
.with_max_retries(1)
.build()
)
.with_requested_check(
RequestedLivenessCheckBuilder()
.for_static_liveness() # New
.with_max_retries(3)
.with_manual_check_never()
.build()
)
# ... other configuration
.build()
)