API Docs · Vision Score
Vision Score
Synchronously runs Vision Score — face liveness and selfie quality — for a set of selfie images, and returns the score with the eKYC application state.
Request
Each image may be supplied inline as base64 or by id (returned from API Upload Image). Vision Score can also be called standalone via the eKYC Core API endpoint; both return a Vision Score, and this one is JWT/encrypted_token-authenticated and returns the same TS-native application shape used across the rest of the eKYC Platform Service API.
The Authorization header carries the JWT access token as Bearer <JWT>, to support request authentication.
| Name | Type | Required | Description |
|---|---|---|---|
images | []ImageData | Required | Selfie images. The last element is treated as the primary selfie. |
gesture_images | []ImageData | Optional | Gesture (active liveness) images. |
videos | []ImageData | Optional | Video frames captured during the liveness flow. |
metadata | json | Optional | Contextual data about the loan application and applicant. See The metadata parameter. |
selfie_type | string | Optional | Selfie liveness type. Common values: passive, flash, active. See The selfie_type parameter. |
flow_id | string | Optional | Routes the request to the lender config mapped to this flow via the encryption key's flow_lender_config_mapping. Defaults to vision_score_only. |
lender_id | int | Optional | Admin-only override that scopes the call to a specific tenant. Non-admin callers always resolve the lender from their JWT and any supplied value is ignored. |
Where each ImageData object accepts either id (preferred — references a pre-uploaded image) or base64 (inline upload):
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Optional | image_id returned by API Upload Image. Use this to avoid re-sending large payloads. |
base64 | string | Optional | Base64-encoded image content without the data:image/jpeg;base64, prefix. Required when id is not supplied. |
metadata | string | Optional | Per-image metadata string forwarded to eKYC. Example: "{\"frame_index\":0}". |
Modes:
| Mode | images | gesture_images | videos | selfie_type | flow_id |
|---|---|---|---|---|---|
| Vision Score only | 1 image | — | — | — | vision_score_only |
| Vision Score with Passive Liveness | 1–3 images | — | optional | passive | vision_score_sanity_liveness |
Request Body:
{
"images": [
{
"id": "8fdcb17e-5d41-4814-8d4f-8c1bfb51df20"
},
{
"base64": "<base64_string>",
"metadata": "{\"frame_index\":1}"
}
],
"gesture_images": [
{
"id": "fa9a4fee-41c3-4988-a126-1431c130aa94"
}
],
"videos": [
{
"id": "b5a23e55-773f-4cf2-985b-835b9b3d0077"
}
],
"metadata": {
"applicant_name": "e3b0c44298fc1c149afb...a1b2c3",
"id_number": "a665a45920422f9d417e...f4c8b1",
"dob": "9f86d081884c7d659a2f...3d5c2a",
"gender": "male",
"phone_number": "b14a7b8059d9c055954c...8e7f12",
"email": "2cf24dba5fb0a30e26e8...9d1c4a",
"application_time": "2025-05-02T10:30:00+07:00",
"loan_amount": 5000000,
"loan_currency": "VND",
"loan_product_type": "personal_loan",
"acquisition_channel": "agent",
"is_existing_customer": false,
"device_type": "android",
"device_model": "Samsung Galaxy A54"
},
"selfie_type": "passive",
"flow_id": "vision_score_only",
"lender_id": 0
}
The metadata parameter
The metadata field accepts a dictionary of contextual data associated with the loan application and applicant. Providing this information enables TS to improve model performance over time and unlocks eligibility for model fine-tuning. All fields are optional. Keys outside the list below are accepted and stored but carry no guaranteed semantic interpretation.
Fields marked Encrypt: Yes contain personally identifiable information. Data must be hashed (SHA-256) or encrypted (AES-256) before transmission, in compliance with applicable data protection regulations (GDPR, PDPA, and equivalent local laws).
| Group | Key | Type | Encrypt | Description |
|---|---|---|---|---|
| Applicant Identity | applicant_name | string | Yes | Full name as captured from ID document |
| Applicant Identity | id_number | string | Yes | National ID or passport number |
| Applicant Identity | dob | string | Yes | Date of birth, ISO 8601 format (e.g. "1990-03-15") |
| Applicant Identity | gender | string | No | Accepted values: "male", "female", "other" |
| Applicant Identity | phone_number | string | Yes | Registered mobile number in E.164 format (e.g. "+84901234567") |
| Applicant Identity | email | string | Yes | Applicant email address |
| Loan Application Context | application_time | string | No | ISO 8601 timestamp of when the loan application was submitted (e.g. "2025-05-02T10:30:00+07:00") |
| Loan Application Context | loan_amount | float | No | Requested loan amount in local currency |
| Loan Application Context | loan_currency | string | No | ISO 4217 currency code (e.g. "VND", "PHP", "USD") |
| Loan Application Context | loan_product_type | string | No | Product category (e.g. "personal_loan", "bnpl", "salary_advance", "sme_loan") |
| Loan Application Context | acquisition_channel | string | No | Channel through which the applicant was acquired (e.g. "organic", "agent", "referral", "digital_ads") |
| Loan Application Context | is_existing_customer | bool | No | Whether the applicant is an existing borrower |
| Device Context | device_type | string | No | Accepted values: "android", "ios", "web" |
| Device Context | device_model | string | No | Device model name (e.g. "Samsung Galaxy A54") |
PII fields below are shown as SHA-256 hashes. Plain-text values must never be transmitted.
{
"metadata": {
"applicant_name": "e3b0c44298fc1c149afb...a1b2c3",
"id_number": "a665a45920422f9d417e...f4c8b1",
"dob": "9f86d081884c7d659a2f...3d5c2a",
"gender": "male",
"phone_number": "b14a7b8059d9c055954c...8e7f12",
"email": "2cf24dba5fb0a30e26e8...9d1c4a",
"application_time": "2025-05-02T10:30:00+07:00",
"loan_amount": 5000000,
"loan_currency": "VND",
"loan_product_type": "personal_loan",
"acquisition_channel": "agent",
"is_existing_customer": false,
"device_type": "android",
"device_model": "Samsung Galaxy A54"
}
}
The selfie_type parameter
selfie_type is the selfie liveness type. These values are shared with the standalone Vision Score endpoint in the eKYC Core API; the request table above names passive, flash and active as the common ones.
selfie_type | Description |
|---|---|
passive | Passive mode |
active | Active mode |
flash | Flash mode with 32 frames |
flash_8 | Flash mode with 8 frames |
flash_16 | Flash mode with 16 framess |
flash_32 | Flash mode with 32 frames |
Response
On success the status code is 200 and the top-level verdict is "success"; see Failure codes for the other values. data.application is present only when an application matching unique_token is found for the lender.
{
"data": {
"unique_token": string, // eKYC application unique token (issued by Platform Init)
"verify_response": { // upstream Platform Verify Face Liveness result
"status": string, // "success" | "failure"
"application_state": {
"current_step": string, // current step in the eKYC flow, e.g. "success"
"fully_completed": bool, // true when every required state is completed
"required_states": [string], // states the flow requires the user to complete
"all_completed_states": [string], // states the user has completed so far
"client_custom_settings": {
"allow_card_types": [string], // card types permitted for this flow
"redirect_url": string // post-flow redirect URL, if configured
}
}
},
"application": { // optional — present only when the application is found
"verdict": string, // "pending" | "approve" | "review" | "reject"
"application_id": int,
"application_unique_token": string,
"application_user_id": string,
"ekyc_face": {
"vision_score": number, // range 0–100; higher = more trustworthy / lower risk
"vision_score_status": string, // "success" | "failure"
"vision_score_request_id": string, // request id for tracing Vision Score logs
"liveness_score": number, // passive-liveness score, range 0–1
"liveness_status": string, // "success" | "failure"
"sanity_check_score": number, // selfie quality score
"sanity_check_result": string, // selfie quality verdict, e.g. "good"
"sanity_check_status": string, // "success" | "failure"
"search_face_result": object, // face-search hits; null when disabled
"search_face_status": string, // e.g. "found"; "" when disabled
"process_status": string, // "success" | "failure"
"processed_at": string, // ISO 8601 timestamp
"selfie_image_uuid": string, // image id of the primary selfie
"gesture_images": [ { "gesture": string, "image_id": string } ],
"sequence_frames": [ ... ], // liveness video frames
"liveness_transformed_video_id": string // video merged from sequence frames
},
"file_ids": { // image/video ids captured during the flow
"selfie_image_uuid": string,
"gesture_images": [ { "gesture": string, "image_id": string } ],
"sequence_frames": [ ... ],
"liveness_transformed_video_id": string
},
"time": string // ISO 8601 timestamp
}
},
"message": string, // human-readable status, e.g. "vision score check completed"
"time": string, // ISO 8601 timestamp of the response
"verdict": string // "success" on HTTP 200; see the error table above for other values
}
| Name | Type | Description |
|---|---|---|
unique_token | string | The eKYC application unique token issued by Platform Init. Use it to look up the application via API Application Detail. |
verify_response | object | The data block returned by the upstream Platform Verify Face Liveness API — same shape as the Selfie Verification API response. |
application | object | Optional — only present when an application matching unique_token is found for the lender. Same shape as the Application Detail response. |
application.verdict follows the same mapping as API Application Detail — pending, approve, review or reject.
The remaining ekyc_face and file_ids fields (compare_score, search_face_result, gesture_images, sequence_frames, liveness_transformed_video_id, …) follow the same shape as the API Application Detail response.
verify_response.application_state reports the flow's progress:
| Name | Type | Description |
|---|---|---|
current_step | string | Current step in the eKYC flow, e.g. "success". |
fully_completed | bool | true when every required state has been completed. |
required_states | []string | States the flow requires the user to complete. |
all_completed_states | []string | States the user has completed so far. |
client_custom_settings | object | Per-client flow settings, e.g. allow_card_types, redirect_url. |
The scoring outputs of this endpoint live under application.ekyc_face. vision_score is the primary output; the liveness and sanity-check fields describe the quality of the captured selfie.
vision_score (0–100). A higher score means a more trustworthy / lower-risk profile.
| Group | Field | Type | Description |
|---|---|---|---|
| Vision Score | vision_score | number | Vision Score, range 0–100. Higher = more trustworthy / lower risk. |
| Vision Score | vision_score_status | string | "success" when the score was computed, otherwise "failure". |
| Vision Score | vision_score_request_id | string | Request id used to trace Vision Score processing in logs. |
| Liveness | liveness_score | number | Passive-liveness score, range 0–1. |
| Liveness | liveness_status | string | "success" or "failure". |
| Sanity check | sanity_check_score | number | Selfie quality score. |
| Sanity check | sanity_check_result | string | Selfie quality verdict, e.g. "good". |
| Sanity check | sanity_check_status | string | "success" or "failure". |
A successful check, with a Vision Score of 86 and an approve verdict:
{
"data": {
"application": {
"application_id": 100010221,
"application_unique_token": "7ae4e342-6cec-4a4e-bd6e-831784391d40",
"application_user_id": "5b506b16-7928-44ea-b690-1ef61d39ed08",
"ekyc_face": {
"gesture_images": [
{
"gesture": "frontal",
"image_id": "186c1ca9-cc99-4fc7-8742-036a2ea8e244"
}
],
"liveness_score": 0.99999034,
"liveness_status": "success",
"liveness_transformed_video_id": "",
"process_status": "success",
"processed_at": "2026-06-17T03:17:22Z",
"sanity_check_result": "good",
"sanity_check_score": 1,
"sanity_check_status": "success",
"search_face_result": null,
"search_face_status": "",
"selfie_image_uuid": "186c1ca9-cc99-4fc7-8742-036a2ea8e244",
"sequence_frames": [],
"vision_score": 86,
"vision_score_request_id": "eec55581-33a0-4d3a-8247-81202609f75b",
"vision_score_status": "success"
},
"file_ids": {
"gesture_images": [
{
"gesture": "frontal",
"image_id": "186c1ca9-cc99-4fc7-8742-036a2ea8e244"
}
],
"liveness_transformed_video_id": "",
"selfie_image_uuid": "186c1ca9-cc99-4fc7-8742-036a2ea8e244",
"sequence_frames": []
},
"time": "2026-06-17T03:17:22Z",
"verdict": "approve"
},
"unique_token": "7ae4e342-6cec-4a4e-bd6e-831784391d40",
"verify_response": {
"application_state": {
"all_completed_states": ["ekyc.selfie.passive_v2"],
"client_custom_settings": {
"allow_card_types": ["vn.national_id"],
"redirect_url": ""
},
"current_step": "success",
"fully_completed": true,
"required_states": ["ekyc.selfie.passive_v2"]
},
"status": "success"
}
},
"message": "vision score check completed",
"time": "2026-06-17T03:17:22Z",
"verdict": "success"
}
Failure codes
data is empty on every error below; the top-level verdict carries the reason.
| Name | Description | Status code | Response Body |
|---|---|---|---|
| Bad Request | Invalid parameters / missing config | 400 | {"data":{},"message":"eKYC platform URL not configured","verdict":"failure"} |
| Unauthorized | Invalid authentication token | 401 | {"data":{},"message":"authorization header is missing","time":"2024-10-31T07:50:22Z","verdict":"missing_authorization"} |
| Token Expired | Token expired. Client should login again | 401 | {"data":{},"message":"Provided token is expired","time":"2024-10-31T07:50:22Z","verdict":"expired_token"} |
| Bad Gateway | Upstream Platform Init / Verify failed | 502 | {"data":{},"message":"platform init failed: ...","verdict":"failure"} / {"data":{},"message":"platform verify selfie failed: ...","verdict":"failure"} |
| Internal Server Error | Cannot create encrypted token | 500 | {"data":{},"message":"cannot create encrypted token","verdict":"failure"} |