Các API của luồng Định danh khách hàng
Định danh khách hàng (eKYC - Electronic Know Your Customer), sau đây gọi là luồng eKYC, là luồng xác minh danh tính khách hàng từ xa, hoàn toàn trên thiết bị của người dùng và không cần gặp trực tiếp. Luồng dùng để xác định người đang thực hiện giao dịch có đúng là chủ của giấy tờ tuỳ thân mà họ cung cấp hay không, thường áp dụng ở bước mở tài khoản hoặc đăng ký sử dụng dịch vụ lần đầu.
Luồng này khác với luồng Xác thực khuôn mặt: eKYC dùng cho lần định danh đầu tiên, có đọc giấy tờ để lấy thông tin và ảnh chân dung. Xác thực khuôn mặt dùng cho các lần sau, chỉ so khớp khuôn mặt với dữ liệu đã được xác thực và lưu sẵn.
Mỗi lần định danh, hệ thống FPT AI eKYC thực hiện các bước sau, theo thứ tự:
- Đọc giấy tờ (OCR - Optical Character Recognition, nhận dạng ký tự quang học): chụp ảnh giấy tờ tuỳ thân (CCCD, CMND, hộ chiếu, giấy phép lái xe...) và tự động bóc tách thông tin như họ tên, ngày sinh, số giấy tờ.
- Kiểm tra mã QR trên CCCD gắn chip: khi ảnh giấy tờ không đọc được mã QR, người dùng quét mã trực tiếp để đối chiếu với thông tin vừa bóc tách.
- Đọc và xác thực chip NFC (Near Field Communication, công nghệ đọc chip không tiếp xúc): đọc dữ liệu trong chip của CCCD gắn chip, kiểm tra dữ liệu có toàn vẹn, không bị sao chép hay chỉnh sửa.
- Kiểm tra thực thể sống (liveness): xác định khuôn mặt trong ảnh hoặc video là của người thật đang đứng trước camera, không phải ảnh in, ảnh hoặc video phát lại trên màn hình, mặt nạ hay khuôn mặt giả mạo bằng công nghệ.
- So khớp khuôn mặt (face match): so khớp khuôn mặt vừa chụp với ảnh chân dung trên giấy tờ, trả về kết quả khớp hoặc không khớp cùng độ tương đồng (
similarity).
Tuỳ nhu cầu, khách hàng có thể bỏ bớt một số bước (ví dụ chỉ đọc giấy tờ, hoặc chỉ kiểm tra khuôn mặt). Các biến thể này được mô tả ở mục Các biến thể luồng.
Một số tình huống sử dụng điển hình
- Mở tài khoản ngân hàng, ví điện tử hoặc tài khoản chứng khoán hoàn toàn trực tuyến.
- Đăng ký dịch vụ tài chính, bảo hiểm hoặc vay tiêu dùng mà vẫn đáp ứng yêu cầu định danh khách hàng.
- Đăng ký tài khoản hoặc dịch vụ của các doanh nghiệp cần xác minh người dùng là người thật và đúng chủ giấy tờ, ví dụ đại lý, cộng tác viên, đối tác.
Điều kiện áp dụng
- Khách hàng đã có
api-keydo FPT cấp và đã cấu hình luồng trên portal. Cấu hình này quyết định các bước nào sẽ chạy trong mỗi phiên (xem mục Cấu hình phiên). - Bước đọc chip NFC chỉ áp dụng khi thiết bị của người dùng hỗ trợ NFC và giấy tờ là CCCD gắn chip.
- Ứng dụng phía khách hàng phải gọi các API đúng thứ tự và dùng chung một mã phiên trong suốt luồng.
Mỗi lần định danh là một phiên eKYC do backend cấp qua API khởi tạo phiên. Mọi API sau đó gửi kèm mã phiên trong header. Tài liệu này dành cho khách hàng tích hợp trực tiếp qua API và mô tả cấu hình không mã hoá payload: request và response ở dạng thường (JSON, form-data), được bảo vệ bằng HTTPS. Với mỗi API, tài liệu nêu thứ tự gọi, header và nội dung (payload).
-
Tổng quan: luồng chuẩn, các biến thể luồng, base URL, header chung, danh sách API và cấu hình phiên.
-
Chi tiết từng API:
-
Xử lý kết quả và mã lỗi: cách đọc kết quả, mã HTTP và mã liveness thường gặp.
-
Phụ lục A. Checklist tích hợp: các lỗi hay gặp khi tích hợp.
Tổng quan
Luồng chuẩn
Luồng mặc định: giấy tờ → (QR) → (NFC) → khuôn mặt.
Sơ đồ tuần tự gọi các API trong luồng Định danh khách hàng
Các bước
- Gọi API 1. Lưu lại
session-idvàsdk_configtrong response. - Chụp ảnh giấy tờ, gửi API 2.
errorCode = "0"là thành công. - Nếu giấy tờ là CCCD gắn chip (trường
Typetrong kết quả OCR bằng8hoặc9), OCR không đọc được QR (trườngQRcodetrống hoặcN/A) vàsdk_config.scan_qr_mode = 1: cho người dùng quét mã QR trên thẻ, gửi API 3. - Nếu
sdk_config.ekyc_flowkhác1, thiết bị có NFC và giấy tờ là CCCD gắn chip: đọc chip, gửi API 4. - Chụp ảnh hoặc quay video khuôn mặt theo
sdk_config.liveness_type, gửi API 5.code = "200"là đạt.
Các biến thể luồng
| Biến thể | Khi nào dùng | Chuỗi API | Ghi chú |
|---|---|---|---|
| Đầy đủ | Mặc định | 1 → 2 → (3) → (4) → 5 | Bước 3, 4 theo điều kiện ở mục Luồng chuẩn |
| Chỉ giấy tờ | Chỉ cần bóc tách giấy tờ | 1 → 2 → (3) | Gửi header only-engine: true ở API 1 và API 2 |
| Chia hai lần | Giấy tờ và khuôn mặt làm ở hai thời điểm khác nhau | Lần 1: 1 → 2. Lần 2: 5 | Lần 2 dùng lại session-id của lần 1, không gọi lại API 1 |
| Chỉ NFC hoặc NFC kèm liveness | Đã có số CCCD, ngày sinh, ngày hết hạn để mở chip | 1 → 4 → (5) | Không có bước OCR |
| Chỉ liveness | Xác thực khuôn mặt, ekyc_flow = 6 | 1 → 6 | Gọi engine liveness trực tiếp |
Thông tin chung
Base URL
| Môi trường | base_url | Dùng cho |
|---|---|---|
| Production | https://api.fpt.ai/vision/ekyc-be/ | Mọi API, trừ API 6 |
| Staging | https://api.fpt.ai/vision/ekyc/be-stag/ | Mọi API, trừ API 6 |
| Engine liveness | https://api.fpt.ai/ | Chỉ API 6 |
- Các đường dẫn trong tài liệu ghép trực tiếp sau
base_url(không có dấu/ở đầu), ví dụbase_url+init_session. - Nếu đi qua proxy riêng của khách hàng, gọi mọi API, kể cả API 6, qua base URL của proxy.
- Timeout khuyến nghị: 60 giây cho kết nối, đọc và ghi.
Header chung
Các header dưới đây xuất hiện ở hầu hết API. Header nào không có giá trị thì bỏ hẳn, không gửi chuỗi rỗng.
| Header | Kiểu dữ liệu | Mô tả |
|---|---|---|
api-key | String | API key do FPT cấp. Bắt buộc ở mọi API |
session-id | String | Mã phiên trả về từ API 1 |
device-type | String | Loại client: android, ios, web-sdk, api. Giữ nguyên một giá trị trong suốt phiên |
sdk-version | String | Phiên bản client, ví dụ 3.7.3.5 |
lang | String | Ngôn ngữ của thông báo và tên trường trong kết quả: vi, en |
manual-time | String | Thời gian người dùng thao tác ở bước hiện tại, tính bằng mili giây, ví dụ 8423 |
x-sdk-trace-id | String | UUID sinh mới cho mỗi request, dùng để truy vết |
x-sdk-trace-values | String | Token xác thực thiết bị (Play Integrity trên Android), gửi ở mọi request. Chưa có token thì gửi -8 |
| Header tuỳ biến | String | Header riêng của khách hàng, ví dụ cho proxy hoặc API gateway. Gửi kèm mọi API |
Header có dấu gạch dưới
API 1 dùng header client_uuid (gạch dưới). Nhiều proxy bỏ header có gạch dưới theo mặc định (nginx cần underscores_in_headers on;). Nếu đi qua proxy riêng, hãy kiểm tra header này tới được backend.
Danh sách API
| API | Method và đường dẫn | Kiểu body | Yêu cầu |
|---|---|---|---|
| 1. Khởi tạo phiên | POST init_session | JSON | Có |
| 2. OCR giấy tờ | POST ocr | form-data | Có, trừ luồng không có OCR |
| 3. Kiểm tra mã QR | POST qrcode | JSON | Tuỳ chọn |
| 4. Kiểm tra dữ liệu chip NFC | POST check_chip | JSON | Tuỳ chọn |
| 5. Liveness và so khớp khuôn mặt | POST face/liveness | form-data | Có |
| 6. Liveness độc lập | POST dmp/liveness/v3 | form-data | Luồng chỉ liveness |
| 7. Ghi log và truy vết | POST logging/tracing | JSON | Tuỳ chọn, chỉ khi enable_tracing = true |
Cấu hình phiên
Response của API 1 trả về cấu hình phiên (trường sdk_config), được thiết lập trên portal theo từng api-key. Client dùng các trường dưới đây để quyết định luồng, tối thiểu cần đọc các trường được đánh dấu ★.
| Trường | Kiểu dữ liệu | Ý nghĩa |
|---|---|---|
ekyc_flow ★ | Int | 1: giấy tờ và khuôn mặt, không NFC. 6: chỉ liveness (API 6). Giá trị khác: có thêm bước NFC (khi thiết bị hỗ trợ NFC và giấy tờ là CCCD gắn chip) |
liveness_type ★ | Int | Kiểu chụp khuôn mặt: 0 PHOTO, 1 VIDEO, 2 ACTIVE_VIDEO, 3 RANDOM, 4 MORE_PHOTO, 5 FLASH. Quyết định payload của API 5 (xem mục 5.3. Request Body) |
scan_qr_mode ★ | Int | 1: bắt buộc quét QR khi OCR không đọc được QR trên CCCD gắn chip |
is_skip_nfc | Boolean | Cho phép người dùng bỏ qua bước NFC |
nfc_retry_limit | Int | Số lần đọc chip thất bại tối đa |
limit | Int | Số lần thử tối đa của phiên (không có trường này thì coi là 5) |
is_show_ekyc_result | Boolean | Hiển thị màn hình kết quả cuối luồng |
enable_tracing | Boolean | Bật ghi log sự kiện phía client (xem API 7). Có thể bỏ qua |
block_screenshot | Boolean | Chặn chụp màn hình trong luồng |
is_upload_image | Boolean | Cho phép chọn ảnh có sẵn thay vì chụp |
show_guide, show_logo, show_label_step, show_action_step, show_switch_button, show_switch_animation, show_exit_dialog, show_nfc_position, font_path, language | Nhiều kiểu | Cấu hình giao diện phía client. Có thể bỏ qua |
document_capture_delay, threshold_yaw, count_stable, iou_threshold, enable_accessibility_service | Nhiều kiểu | Tham số chụp tự động và kiểm tra thiết bị phía client. Có thể dùng làm gợi ý |
Trường on_device_model_config (cùng cấp với sdk_config) bật hoặc tắt các kiểm tra chất lượng ảnh trên thiết bị: blur, detection, brightness, face_quality, eye_closed. Client có thể dùng làm gợi ý.
1. API khởi tạo phiên
Áp dụng cho: mọi luồng. Kiểu body: JSON.
Tạo phiên eKYC mới. Trả về mã phiên và cấu hình phiên.
1.1. Request URL
POST base_url/init_session
1.2. Request Headers
| Tham số | Yêu cầu | Kiểu d ữ liệu | Mô tả |
|---|---|---|---|
| api-key | Có | String | API key do FPT cấp |
| device-type | Có | String | android, ios, web-sdk, api |
| Content-Type | Có | String | application/json; charset=utf-8 |
| client_uuid | Không | String | Mã do hệ thống khách hàng sinh cho mỗi lần định danh, dùng để đối soát phiên, ví dụ một UUID |
| sdk-version | Không | String | Phiên bản client |
| only-engine | Không | Boolean | true: luồng chỉ giấy tờ. Mặc định false |
| x-sdk-trace-values | Không | String | Thường là -8 ở bước này vì token thiết bị chỉ lấy được sau khi có session-id |
1.3. Request Body
Thông tin thiết bị dạng JSON.
| Tham số | Yêu cầu | Kiểu dữ liệu | Mô tả |
|---|---|---|---|
| memory | Không | String | Dung lượng RAM của thiết bị (GB), ví dụ "5.62" |
| model | Không | String | Chuỗi ghép từ model, phiên bản hệ điều hành và hãng, phân tách bằng ký tự gạch đứng. Xem ví dụ trong mẫu request |
| nfc_support | Không | Boolean | Thiết bị có NFC hay không |
| root | Không | Boolean | Client phát hiện thiết bị root hoặc jailbreak, hoặc công cụ can thiệp (Frida...) |
| emulator | Không | Boolean | Client phát hiện đang chạy trên máy ảo |
| debugger | Không | Boolean | Client phát hiện đang gắn debugger |
| accessibility | Không | Boolean | Có dịch vụ trợ năng (accessibility) đang bật |
1.4. Mẫu request
curl --location --request POST 'base_url/init_session' \
--header 'api-key: your-api-key' \
--header 'client_uuid: 3f1c2b9e-6a1d-4f0e-9d7a-2b8e5c4a1f90' \
--header 'device-type: android' \
--header 'sdk-version: 3.7.3.5' \
--header 'only-engine: false' \
--header 'x-sdk-trace-values: -8' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"memory": "5.62",
"model": "SM-S918B|14|samsung",
"nfc_support": true,
"root": false,
"emulator": false,
"debugger": false,
"accessibility": false
}'
1.5. Response
1.5.1. Mẫu response
Thành công: http_code = 200
{
"code": "200",
"message": "success",
"session-id": "7760a743-884b-4195-b781-892e72c7b0b6",
"sdk_config": {
"ekyc_flow": 2,
"liveness_type": 4,
"scan_qr_mode": 1,
"is_skip_nfc": false,
"nfc_retry_limit": 3,
"limit": 5,
"is_show_ekyc_result": true,
"enable_tracing": true,
...
},
"on_device_model_config": {
"blur": true,
"detection": true,
"brightness": 0.3,
"face_quality": true,
"eye_closed": true
}
}
Thất bại: http_code != 200
1.5.2. Mô tả response
| Tham số | Kiểu dữ liệu | Mô tả |
|---|---|---|
| code | String | "200": thành công. Giá trị khác: hiển thị message và dừng luồng |
| message | String | Thông báo trả về |
| action | String | Có giá trị exit khi phải dừng luồng (xem phần Xử lý kết quả và mã lỗi) |
| session-id | String | Mã phiên. Gửi trong header session-id của mọi API sau |
| sdk_config | Object | Cấu hình phiên, xem mục Cấu hình phiên |
| on_device_model_config | Object | Cấu hình kiểm tra chất lượng ảnh trên thiết bị |
1.5.3. Mẫu response lỗi
| HTTP | Response | Nguyên nhân |
|---|---|---|
| 200 | {"code": "<khác 200>", "message": "..."} | Không tạo được phiên, hiển thị message |
| 401 | {"code": "401", "message": "..."} | api-key không hợp lệ |
| 422 | {"detail": [...]} | Thiếu header bắt buộc |
| 500 | – | Lỗi hệ thống |
2. API OCR giấy tờ
Áp dụng cho: luồng đầy đủ, luồng chỉ giấy tờ. Kiểu body: form-data.
Bóc tách thông tin trên giấy tờ tuỳ thân. Mặt trước và mặt sau gửi trong một request (cách A, mục 2.4) hoặc hai request riêng (cách B).
2.1. Request URL
POST base_url/ocr
2.2. Request Headers
| Tham số | Yêu cầu | Kiểu dữ liệu | Mô tả |
|---|---|---|---|
| api-key | Có | String | API key do FPT cấp |
| session-id | Có | String | Mã phiên từ API 1 |
| device-type | Có | String | Cùng giá trị đã dùng ở API 1 |
| document-type | Có | String | Loại giấy tờ, xem bảng ở mục 2.3 |
| country | Không | String | Quốc gia phát hành giấy tờ. Mặc định vi |
| lang | Không | String | vi, en |
| only-engine | Không | Boolean | Cùng giá trị đã dùng ở API 1 |
| side-type | Không | String | front hoặc back, chỉ dùng khi gửi từng mặt riêng (mục 2.4, cách B). Ảnh mặt trước phải được gửi trước |
| sdk-version | Không | String | Phiên bản client |
| manual-time | Không | String | Tổng thời gian chụp mặt trước và mặt sau (ms) |
| x-sdk-trace-id, x-sdk-trace-values | Không | String | Xem mục Header chung |
2.3. Request Body
FormData, đúng thứ tự sau:
| Thứ tự | Part | Yêu cầu | Kiểu | Mô tả |
|---|---|---|---|---|
| 1 | files | Có | File | Ảnh mặt trước, image/jpeg |
| 2 | files | Theo giấy tờ | File | Ảnh mặt sau |
Giá trị của document-type và số ảnh tương ứng:
| document-type | Giấy tờ | Số ảnh |
|---|---|---|
idr | CCCD / CMND | 2 (mặt trước, mặt sau) |
dlr | Giấy phép lái xe | 2 khi country = vi, 1 (mặt trước) với quốc gia khác |
passport | Hộ chiếu | 1 (trang thông tin) |
umidr | UMID (Philippines) | 1 |
tinidr | TIN ID (Philippines) | 1 |
Ảnh: JPEG, khuyến nghị thu nhỏ về tối đa 1080 × 1080.
2.4. Mẫu request
Cách A (khuyến nghị): gửi hai mặt trong một request.
curl --location --request POST 'base_url/ocr' \
--header 'api-key: your-api-key' \
--header 'session-id: 7760a743-884b-4195-b781-892e72c7b0b6' \
--header 'device-type: android' \
--header 'document-type: idr' \
--header 'country: vi' \
--header 'lang: vi' \
--header 'only-engine: false' \
--header 'sdk-version: 3.7.3.5' \
--header 'manual-time: 8423' \
--header 'x-sdk-trace-id: 0b6e1f43-2a52-4c1b-9b0e-5f4d3c2a1b00' \
--header 'x-sdk-trace-values: <token>' \
--form 'files=@"front.jpg";type=image/jpeg' \
--form 'files=@"back.jpg";type=image/jpeg'
Cách B: gửi từng mặt, mỗi request một ảnh kèm header side-type, mặt trước gửi trước. Nếu document-type là passport thì mỗi request chỉ gửi 1 ảnh.
2.5. Response
2.5.1. Mẫu response
Thành công
{
"errorCode": "0",
"errorMessage": "",
"data": [
{ "key": "ID", "name": "Số/ No", "value": "0010xxxxxxxx", "score": "98.66", "locale": "vn" },
{ "key": "Name", "name": "Họ và tên", "value": "NGUYEN VAN A", "score": "99.71", "locale": "vn" },
{ "key": "Date of birth", "name": "Ngày sinh", "value": "01/01/1990", "score": "98.41", "locale": "vn" },
{ "key": "Type", "name": "Loại thẻ", "value": "8", "score": "", "locale": "vn" },
{ "key": "QRcode", "name": "QR code", "value": "0010xxxxxxxx|...", "score": "", "locale": "vn" },
...
]
}
2.5.2. Mô tả response
| Tham số | Kiểu dữ liệu | Mô tả |
|---|---|---|
| errorCode | String | "0" là thành công. Giá trị khác là lỗi, hiển thị errorMessage cho người dùng chụp lại. Chi tiết xem tài liệu mã lỗi |
| errorMessage | String | Thông báo lỗi theo ngôn ngữ của header lang |
| action | String | exit: dừng luồng, không cho chụp lại |
| data | Array | Các trường bóc tách, mỗi phần tử gồm key, name, value, score, locale. Danh sách trường phụ thuộc loại giấy tờ |
| total_data | Array | Danh sách trường mở rộng (nếu có), cùng cấu trúc data |
| list_tables, table_title | Array, String | Dữ liệu dạng bảng (nếu có) |
Các key dùng để điều hướng luồng (so khớp không phân biệt hoa thường):
| key | Dùng để |
|---|---|
Type | 8 hoặc 9: CCCD gắn chip, có thể sang bước QR (API 3) hoặc NFC (API 4) |
QRcode | Trống hoặc N/A: OCR không đọc được QR, quét QR nếu scan_qr_mode = 1 |
ID, Date of birth, Expired Date, MRZ | Lấy khoá mở chip NFC (số CCCD, ngày sinh, ngày hết hạn) |
2.5.3. Mẫu response lỗi
Lỗi nghiệp vụ: HTTP 200, errorCode khác "0"
{
"errorCode": "3",
"errorMessage": "Không tìm thấy tài liệu đã chọn trong ảnh"
}
Phiên làm việc hết hạn: trả về khi phiên không hợp lệ, ví dụ phiên chưa được đăng ký, phiên hết thời gian chờ, không khớp loại thiết bị...
{
"errorCode": "403",
"errorMessage": "Phiên làm việc đã hết hạn"
}
Các trường hợp lỗi theo mã HTTP:
| HTTP | Response | Nguyên nhân |
|---|---|---|
| 403 | {"errorCode": "403", "errorMessage": "Your device is not secure..."} | Thiết bị không an toàn theo token xác thực thiết bị |
| 403 | {"errorCode": "403", ...} | Phiên không tồn tại, hết hạn hoặc đã kết thúc |
| 422 | {"detail": [...]} | Thiếu part hoặc header bắt buộc |
3. API kiểm tra mã QR
Áp dụng cho: CCCD gắn chip. Kiểu body: JSON. Yêu cầu: tuỳ chọn.
Gửi chuỗi QR quét được trên CCCD gắn chip, dùng khi ảnh OCR không đọc được QR. Hệ thống so khớp dữ liệu QR với kết quả OCR ở bước trước.
3.1. Request URL
POST base_url/qrcode
3.2. Request Headers
| Tham số | Yêu cầu | Kiểu dữ liệu | Mô tả |
|---|---|---|---|
| api-key | Có | String | API key do FPT cấp |
| session-id | Có | String | Mã phiên từ API 1 |
| device-type | Có | String | Cùng giá trị đã dùng ở API 1 |
| Content-Type | Có | String | application/json; charset=utf-8 |
| lang | Không | String | vi, en |
| sdk-version, manual-time, x-sdk-trace-id, x-sdk-trace-values | Không | String | Xem mục Header chung |
3.3. Request Body
JSON.
| Tham số | Yêu cầu | Kiểu dữ liệu | Mô tả |
|---|---|---|---|
| qrcode | Có | String | Nguyên văn chuỗi đọc được từ mã QR trên CCCD |
3.4. Mẫu request
curl --location --request POST 'base_url/qrcode' \
--header 'api-key: your-api-key' \
--header 'session-id: 7760a743-884b-4195-b781-892e72c7b0b6' \
--header 'device-type: android' \
--header 'lang: vi' \
--header 'sdk-version: 3.7.3.5' \
--header 'manual-time: 3120' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
"qrcode": "0010xxxxxxxx|0123456789|NGUYEN VAN A|01011990|Nam|...|01012021"
}'
3.5. Response
3.5.1. Mẫu response
Thành công: http status = 200 và errorCode = 0
{
"errorCode": "0",
"errorMessage": ""
}
Thất bại
Lỗi dữ liệu QR không khớp:
{
"errorCode": "QR01",
"errorMessage": "Dữ liệu trong mã QR không khớp"
}
Phiên làm việc hết hạn: trả về khi phiên không hợp lệ, ví dụ phiên chưa được đăng ký, phiên hết thời gian chờ, không khớp loại thiết bị...
{
"errorCode": "403",
"errorMessage": "Phiên làm việc đã hết hạn"
}