Face · Standard API · eKYC Core
Verify face liveness
Checks whether the selfie images, gesture images or videos were captured from a live person, and returns an is_live verdict with a liveness score.
Request
Clients can provide image data to the TrustVision API by specifying the ID the image by using upload API, or by sending the image data as base64-encoded text.
| Name | Type | Required | Description |
|---|---|---|---|
images | []image | Required | array of frontal image object contains properties as below table. Active liveness: 2-4 images, Passive: 1-3 images. Any flash mode (flash, flash_8, flash_16, flash_32, flash_edge, flash_advanced) and passive_v2 need multiple images covering the captured frames — a single still image gives the model nothing to score. |
gesture_images | []gesture_image | Optional | array of gestures contain images. Active liveness: 3 images, Passive: no gesture image. |
videos | []video | Optional | array of video recording when liveness on SDK. Default 1 |
metadata | dictionary | Optional | the collected data during liveness checking process |
selfie_type | string | Optional | Specify the characteristic of a disabled face or liveness mode eg. disabled eye, passive, active, flash. Must be set explicitly to match the captured data — sending multiple images without setting this to the matching flash value (or passive_v2) means that liveness model never runs against them. |
- For image, if you use
base64,labelandmetadata, omit parameterid. If you useid(resultidis returned from Upload Image API), omit parameterbase64,labelandmetadata. - For video, if you use
id(resultidis returned from Upload File API), omit parameterframes,metadata. If you useframes,metadata, omit parameterid. - If you use
gesture_images, theimagesparameter should have at least 2 images andgestureingesture_imagesshould have at leastleftorright.
{
"images": [
{
"id": string,
"base64": string,
"label": string,
"metadata": string_json,
},
{
"id": string,
"base64": string,
"label": string,
"metadata": string_json,
}
],
"gesture_images":[
{
"gesture": string,
"images": [
{
"id": string,
"base64": string,
"label": string,
"metadata": string_json,
},
{
"id": string,
"base64": string,
"label": string,
"metadata": string_json,
}
],
}
],
"videos": [
{
"id": string,
"metadata": string_json,
"frames": [
{
"base64": string,
"label": string,
"index": int,
"metadata": string_json,
},
{
"base64": string,
"label": string,
"index": int,
"metadata": string_json,
},
// other frames
],
},
{
"id": string,
"metadata": string_json,
"frames": [
{
"base64": string,
"label": string,
"index": int,
"metadata": string_json,
},
{
"base64": string,
"label": string,
"index": int,
"metadata": string_json,
},
// other frames
],
}
],
"metadata": dictionary
}
Which each image's parameters contains:
| key | type | required | description |
|---|---|---|---|
id | string | Optional | ID of image is returned from API Upload |
base64 | string | Optional | base64 encoded text of image data |
label | string | Optional | label of the image as described at Label table |
metadata | string_json | Optional | key-value string, key should be string, value should be string, int, float, bool. Example "{\"id\":\"123456789\",\"type\":1}" |
Which each video's parameters contains:
| key | type | required | description |
|---|---|---|---|
id | string | Optional | ID of video is returned from API Upload |
metadata | string_json | Optional | key-value string, key should be string, value should be string, int, float, bool. Example "{\"id\":\"123456789\",\"type\":1}" |
frames | []FrameData | Optional | list of video frames to be uploaded |
Which FrameData's parameters contains:
| key | type | required | description |
|---|---|---|---|
base64 | string | Optional | base64 encoded text of image data |
label | string | Optional | label of the image as described at Label table |
index | int | Optional | the index of this frame in the video |
metadata | string_json | Optional | key-value string, key should be string, value should be string, int, float, bool. Example "{\"id\":\"123456789\",\"type\":1}" |
Which each gesture_image's parameters contains:
| key | type | required | description |
|---|---|---|---|
gesture | string | Required | specify a gesture value as below table. Example left, right, up or down |
images | []image | Required | an array of images for each gesture |
Which gesture parameter contains value:
| gestures | description |
|---|---|
left | the face turns left |
right | the face turns left |
up | the face up |
down | the face down |
The selfie_type parameter
selfie_type names the liveness mode the capture used, and is defined as one of the table values below.
| selfie_type | description |
|---|---|
id_card | Portrait image in Chip NFC of CCCD |
light | Light Authen |
passive | Standard Authen |
passive_v2 | Standard Authen (v2) |
active | Advanced Authen uses active liveness (gestures) |
flash | Advanced Authen uses full flash liveness |
flash_8 | Edge Authen uses flash liveness with 8 frames |
flash_16 | Edge Authen uses flash liveness with 16 frames |
flash_32 | Advanced Authen uses flash liveness with 32 frames |
flash_edge | Edge Authen uses flash liveness with minimum time |
flash_advanced | Advanced Authen uses full flash liveness |
Sample request
curl -X POST \
https://tv-staging.trustingsocial.com/api/v1/verify_face_liveness_sync \
-H 'Authorization: TV <YOUR ACCESS KEY>:<CREATED SIGNATURE>' \
-H 'X-TV-Timestamp: 2019-04-21T18:00:15+07:00' \
-H 'Content-Type: application/json' \
-d \
'
{
"images": [
{
"id": "a4facba3-334b-41fb-97e3-4766cb70ae29"
},
{
"id": "c5a2a405-f152-4248-b746-3af76b241ef7"
},
{
"id": "5d0345f7-da7c-44e8-bd6c-a8c08c806227"
}
],
"gesture_images": [
{
"gesture": "left",
"images": [
{
"id": "d849a3ce-d209-4605-99a7-3d369234fd5e"
}
]
},
{
"gesture": "up",
"images": [
{
"id": "551fc06a-14fd-44dc-8838-abe895007c36"
}
]
},
{
"gesture": "right",
"images": [
{
"id": "536d2457-bfc0-4249-85f2-8c4053bdec58"
}
]
}
],
"videos": [
{
"id": "433331b5-e1d4-433f-84e6-2260ec6bee92"
},
{
"id": "3b4bf7b6-088b-4931-9279-9259f5c34fe3"
}
]
}
'
A Flash mode request sends multiple images — one per captured frame — and sets selfie_type to the matching flash value, instead of the single image a passive request would use:
curl -X POST \
https://tv-staging.trustingsocial.com/api/v1/verify_face_liveness_sync \
-H 'Authorization: TV <YOUR ACCESS KEY>:<CREATED SIGNATURE>' \
-H 'X-TV-Timestamp: 2019-04-21T18:00:15+07:00' \
-H 'Content-Type: application/json' \
-d \
'
{
"selfie_type": "flash_16",
"images": [
{
"id": "b635e407-13e4-403b-8731-80821cfa4922"
},
{
"id": "1cb8712c-56da-4c26-a850-08618248a370"
}
],
"gesture_images": []
}
'
Response
data.is_live carries the verdict and data.score how likely it is that the face is live (0-1). In case the request processing has been finished, the HTTP status code will be 200, and the data.status is either "success" or "failure", depending on whether the request has been successfully processed or not.
{
"data": {
"status": string, "success" or "failure"
"is_live": bool, // the face is live or not
"score": float // how likely that the face is live (0-1)
"images": [
{
"id": string, // ID of the image
"is_live": bool, // liveness result of this image only
"score": float // how likely that the face is live (0-1)
},
... // up to 3 images
],
"main_image_id": string, // optional return main image id use to compare faces and face retrieval
"request_id": string // return request id to the client and can use request-id to check logs.
},
"errors": [
{
"code": string,
"message": string,
"detail": {
"field": string, // optional, which parameter is invalid.
... // any other information that can be useful for client
},
},
... // other errors
]
}
A live selfie captured over six frames comes back like this:
{
"data": {
"details": null,
"images": [
{
"id": "0e33129a-c9ec-4e22-bf53-78d496bb1f37",
"is_live": true,
"score": 0.981541368665434
},
{
"id": "9687e2c9-4f99-46e8-a4d9-4469d61de829",
"is_live": true,
"score": 0.9879801868228469
},
{
"id": "007b943c-eda3-4b08-8c80-da258cb69eac",
"is_live": true,
"score": 0.9843504686937438
},
{
"id": "d98d5b5c-30e0-43c2-b812-7d53f0fc585d",
"is_live": true,
"score": 0.9493469735404098
},
{
"id": "010b5aa5-ef2a-47fe-937d-b5d10dfad443",
"is_live": true,
"score": 0.9687573370988268
},
{
"id": "845c3a3b-623a-4de0-8f13-e43115239e5b",
"is_live": true,
"score": 0.9464235386207961
}
],
"is_live": true,
"request_id": "93029850-924c-465a-baa6-27d843cbb1ba",
"score": 0.9999999862517992,
"status": "success",
"videos": [
{
"is_live": true,
"score": 1
}
]
}
}
A Flash mode request (selfie_type: "flash_16") scores every image individually; there is no videos key since none was sent:
{
"data": {
"details": null,
"images": [
{
"id": "1cb8712c-56da-4c26-a850-08618248a370",
"is_live": true,
"score": 0.910237543284893
},
{
"id": "b635e407-13e4-403b-8731-80821cfa4922",
"is_live": true,
"score": 0.9999598160466121
}
],
"is_live": true,
"request_id": "e46ebc4e-9e20-44e1-8fc9-bb3fa7baac4a",
"score": 0.910237543284893,
"server_infos": {
"timestamp": "2026-08-05T15:46:18+07:00"
},
"status": "success"
}
}
Failure codes
If the data.status is "failure", the "errors" field will tell you why it failed.
| Error code | Description |
|---|---|
image_has_no_face | the input image has no faces |
In case of any other errors, the data field will be empty, and the server sends one of following HTTP status code with error code:
| HTTP code | Error code | Description |
|---|---|---|
401 | access_denied_exception | You are not authorized to perform the action. |
413 | image_too_large_exception | The input image size exceeds the allowed limit (15MB). |
415 | invalid_image_format_exception | The provided image format is not supported (JPG/PNG) |
400 | invalid_parameter_exception | Input parameter violates a constraint. |
400 | request_time_too_skewed | The X-TV-Timestamp header is expired, need a newer one. |
404 | image_not_found_exception | The image ID is not found in DB. |
408 | request_timeout_exception | Request takes too long to process |
429 | rate_limit_exception | The number of requests exceeded your throughput limit. |
500 | internal_server_error | Some unexpected error occurs while processing the request |
The codes above are the ones documented for this endpoint. Response errors on the Overview lists the codes for the eKYC Core surface as a whole — the two lists do not match on every endpoint.