NAV Navbar
cURL JSON CodeSnippet

Revision History

Sl# Ver# Modified By Update Updated On
1 1.0 Tech Team - Initial Draft 29/05/2019

1. Introduction

Khosla Labs’ Veri5Digital Platform VideoIdKyc API provides various features related to the requirement of completing Know-Your-Customer (KYC) by companies. Video ID KYC APIs are standalone APIs which can be used by clients to digitize their customer onboarding process. Khosla Labs’ Video ID KYC product allows for digital, instant and paperless Know Your Customer (KYC) capability.

KYC is a tedious and costly activity required by businesses for their regulatory compliance. Traditionally, KYC was done in a Face-to-Face manner where the business’ employees, agents or Business Correspondents (BCs) would do KYC.

KYC process involves two primary activities - Identification (Identifying customer details) and Authentication (confirming details belong to the same individual). Khosla Labs’ Video ID KYC solution provides the capability to perform above KYC process in a completely digital fashion which eliminates the need for a physical visit and automates the process using easily integratable APIs which you can call from your Web or Mobile application.

The list of core APIs include :

2. Veri5 VideoIdKyc API Options

Video ID KYC API collection will have the following APIs:

  1. docInfoExtract

  2. faceExtract

  3. faceCompare

2.1 DocInfoExtract

This API will do a full Optical Character Recognition (OCR) by scanning single or both sides of an identity document and automatically extract the details for you. The documents that are currently being accepted are:

You can send an ID Card image along with the type of ID Card to this API, and it will do 2 things

Using the above API, you can confirm the authenticity of the ID Card document and also get digitized data on the ID Card and store in your systems. Currently, we support Government ID Cards such as PAN, Aadhaar, Voter ID and Passport. We are in the process of adding other IDCards in subsequent releases.

curl -X POST \
  https://sandbox.veri5digital.com/video-id-kyc/api/1.0/docInfoExtract \
  -H 'Content-Type: application/json' \
  -H 'cache-control: no-cache' \
  -d '{
    "headers": {
        "client_code": "<<client_code>>",
        "sub_client_code": "dummy-code",
        "channel_code": "ANDROID_SDK",
        "channel_version": "1.0.0",
        "stan": "<<unique_stan>>",
        "client_ip": "<<client_ip>>",
        "transmission_datetime": "1428393481435",
        "operation_mode": "SELF",
        "run_mode": "TRIAL",
        "actor_type": "OTHER",
        "user_handle_type": "EMAIL",
        "user_handle_value": "a@b.com",
        "location": "NA",
        "function_code": "DEFAULT",
        "function_sub_code": "DEFAULT"
    },
    "request": {
        "api_key": "<<api-key>>",
        "request_id": "<<unique_request_id>>",
        "purpose": "testing in Khosla",
        "hash": "b9bbb3e188faae7190a41be8f35fd85ed0f49a093cb8f9ff916f29b1a9fe0749",
        "document_type": "AADHAAR",
        "document_side": "FRONT",
        "extraction_type": "BOTH",
        "document_front_image": "<<base64image>>",
        "document_back_image": "<<base64image>>"
    }
}'

2.2 Face Extract

This API will extract a face image embedded in a document or ID. Input for this API will be a single image.

You can use this API to replace sticking passport photos from Account Opening Forms and make the process digital.

Use this API to get the photo from ID Card or any other document and store in your systems according to business requirements.

2.3 FaceCompare

Authenticate the Identity through Face Biometrics based match. Check the likelihood that two faces belong to the same person through our AI-driven Face Compare API. This API will take 2 images as input. You can send 2 images through this API, we would use our ML-driven Face match algorithms to get back with Match Status and Accuracy %. You can use this API to check if the photo on the ID Card is the same as the photo of the person.

curl -X POST \
  https://sandbox.veri5digital.com/video-id-kyc/api/1.0/faceCompare \
  -H 'Content-Type: application/json' \
  -H 'Postman-Token: 259f5ce7-e3fe-41cb-8524-38b11084880a' \
  -H 'cache-control: no-cache' \
  -d '{
    "headers": {
        "client_code": "<<client_code>>",
        "sub_client_code": "<<client_code>>",
        "channel_code": "ANDROID_SDK",
        "channel_version": "1.0.0",
        "stan": "<<unique_stan>>",
        "client_ip": "49.205.221.149",
        "transmission_datetime": "1428393481435",
        "operation_mode": "SELF",
        "run_mode": "TRIAL",
        "actor_type": "OTHER",
        "user_handle_type": "EMAIL",
        "user_handle_value": "a@b.com",
        "location": "NA",
        "function_code": "DEFAULT",
        "function_sub_code": "DEFAULT"
    },
    "request": {
        "api_key": "<<api-key>>",
        "request_id": "<<request_id>>",
        "purpose":"Testing",
        "hash": "357a8931d6ea23e2bb341f45e57805070c9b35c73ae276631b120b588d390404",
        "image_1": "<<img_1>>",
        "image_2": "<<img_2>>"
    }
}'

3. Prerequisite

For a client to get access to APIs, client registration is mandatory for the chosen environment, ie either SandBox or Production. Based on the chosen environment , APIs will be accessible.

Its recommended to gain access to SandBox Environment and do technical integration and testing before accessing the same in Production Environment.

The client has to obtain the following values for the following params :

For invoking API Intent call client_code, api_key, and hash have to be mandatorily passed

Refer Appendix A for Request Headers Information.

Refer Appendix B to see how to calculate the hash and encryption mechanism.

Refer Appendix C for Input Image Specifications for achieving better results.

Refer Appendix D for the encryption mechanism

Refer Appendix E for the Certificates Generation for response encryption

Refer Appendix F for Error Codes

4. API Specification

Khosla Lab’s APIs are exposed as REST Endpoints. Request and Response Payloads are JSON documents. API communications should be made over HTTPS.

APIs are exposed to consumers in different environments like SandBox and Production. The primary purpose of the sandbox environment is to expose the APIs to consumers where they can test and do technical integrations before moving to actual production.

4.1 API - docInfoExtract

The primary objective of this API is to extract the KYC information from the document image..

At a high level below given is the specification.

### 4.1.1 Request Payload

Method : POST

Url : https://<hostname>/video-id-kyc/api/1.0/docInfoExtract


{
    "headers": // Please refer Appendix A for details.
            {
              "client_code": "<your_client_code>",
              "sub_client_code": "<your_sub_client_code>",
              "channel_code": "", // Can be Empty
              "channel_version": "", // Can be Empty
              // This should be a unique alphanumeric string generated by the calling system.
              "stan": "<Unique Alphanumeric String>",
              "client_ip": "",
              "transmission_datetime": "",
              "operation_mode": "SELF",
              "run_mode": "",
              "actor_type": "",
              "user_handle_type": "",
              "user_handle_value": "",
              "location": "", 
              "function_code": "DEFAULT",
              "function_sub_code": "DEFAULT"
            },
            "request": {
                      "api_key": "<your_api_key>",
                      "document_type": "<PAN|AADHAAR|VOTER_ID|PASSPORT>",
                      "document_side": "<FRONT|BOTH>",
                      "extraction_type": "<FACE|OCR|BOTH>",
                      "document_front_image": "<base_64_encoded_string>",
                      "document_back_image": "<base_64_encoded_string>"
                    }
}
### 4.1.2 Response Payload

Response:

{
    "response_data": {
    "encrypted": "<YES|NO>",
    // This flag indicates whether the Kyc response is Encrypted or Not. If encrypted is ‘YES’, refer to Appendix D for decryption logic
    "hash": "", // Generated Hash of the response payload
    "document_data": "" // Please refer below for the details.
    },
    "response_status": {
      "status": "<SUCCESS|FAIL>",
      "code": "",
      "message": ""
      }
}

Document_data

In case document_data is encrypted ( based on flag “encrypted” : “ ), please refer to Appendix D and Appendix E for decryption logic and creation of public and private keys for encryption.

This field encapsulates the KYC data extracted from the ID Document. By default, it will be an encoded JSON, whose structure is given below. Depending on the encrypted flag as “YES/NO”, this encoded string will be either encrypted or not.

Details of document_data are as given below :

{
  "photo":{
            "document_image":"" // Image of the input ID Document
          },
  "original_kyc_info":{
          // This tag encapsulated the OCRed data from the ID Card
          // Based on the ID Card Type, any of the following information will be present or not.
          "doc_type":"", // Possible values : PAN|AADHAAR|PASSPORT|VOTER_ID
          "first_name":"",
          "second_name":"",
          "dob":"",
          "document_id":"", // ID Docs Number. Eg : PAN#, Aadhaar#
          "address":"",
          "gender":"",
        },
  "doc_image":{
         // Any of the below-mentioned parameters (Based on Request)
         "pan":"",
         "aadhaar_front":"",
         "aadhaar_back":"",
         "passport_front":"",
         "passport_back":"",
         "voter_id_front":"",
         "voter_id_back":"",
        },
  "quality_check":{
                 "front_image":{
                                "image_aspect_ratio":"",
                                "image_exposure":"",
                                "image_blur":"",
                                "image_colour":"",
                                "image_reflection":""
                               },
                "back_image":{
                             "image_aspect_ratio":"",
                             "image_exposure":"",
                             "image_blur":"",
                             "image_colour":"",
                            "image_reflection":""
                            }
                 }
}

4.2 API - faceExtract

The primary objective of this API is to extract the face from the input Id image.

Method : POST

Url : https://hostname/video-id-kyc/api/1.0/faceExtract


### 4.2.1 Request Payload
{
    "headers": // Please refer Appendix A for details.
    {
      "client_code": "<your_client_code>",
      "sub_client_code": "<your_sub_client_code>",
      "channel_code": "",
      "channel_version": "",
      "stan": "<a_unique_integer>",
      "client_ip": "",
      "transmission_datetime": "",
      "operation_mode": "SELF",
      "run_mode": "",
      "actor_type": "",
      "user_handle_type": "",
      "user_handle_value": "",
      "location": "",
      "function_code": "DEFAULT",
      "function_sub_code": "DEFAULT"
    },
    "request": {
              "api_key": "<your_api_key>",
              "request_id": "<REQUEST_ID>",
              "purpose":"<PURPOSE>",
              "hash": "Refer Appendix B",
              "image": "<base_64_encoded_string>"
              }
}
### 4.2.2 Response Payload
{
      "response_data": {
                      "encrypted": "<YES|NO>",
                      "hash": "",
                      "face": "" // Base64 Encoded image of the extracted face.
                      },
      "response_status": {
                      "status": "<SUCCESS|FAIL>",
                      "code": "",
                      "message": ""
                        }
}

In case face is encrypted ( based on flag “encrypted” : “ ), please refer to Appendix D and Appendix E for decryption logic and creation of public and private keys for encryption.

4.3 API - faceCompare

This API can be used to compare two face images. Input Images can be

Method : POST

Url : https://hostname/video-id-kyc/api/1.0/faceCompare

### 4.3.1 Request Payload

{
      "headers": // Please refer Appendix A for details.
      {
        "client_code": "<your_client_code>",
        "sub_client_code": "<your_sub_client_code>",
        "channel_code": "",
        "channel_version": "",
        "stan": "<a_unique_integer>",
        "client_ip": "",
        "transmission_datetime": "",
        "operation_mode": "SELF",
        "run_mode": "",
        "actor_type": "",
        "user_handle_type": "",
        "user_handle_value": "",
        "location": "",
        "function_code": "DEFAULT",
        "function_sub_code": "DEFAULT"
      },
      "request": {
            "api_key": "<your_api_key>",
            "request_id": "<REQUEST_ID>",
            "purpose":"<PURPOSE>",
            "hash": "Refer Appendix B",
            "image_1": "<base_64_encoded_string>",
            "image_2": "<base_64_encoded_string>"
                }
}
### 4.3.2 Response Payload

{
    "response_data": {    
                "status": "",
                "hash": "",
                "score": ""
    },
    "response_status": {
                "status": "<SUCCESS|FAIL>",
                "code": "",
                "message": ""
    }
}

Appendix A: Request Header Field Details

Every APIs Request Payload has a custom “header” field, whose details are given below. Fields marked ( * ) are mandatory.

S.NO Field Name Mandatory Description Example
1 client_code* A unique code assigned to a client while onboarding. client123
2 sub_client_code* Unique code assigned to a sub client of a hosted client.In case not applicable, an EMPTY (“”) string can be passed. client123
3 actor_type* Type of person who is making the request.If not relevant, "DEFAULT" can also be used. CUSTOMER/DEFAULT
4 channel_code* Channel code used by the client to initiate the request. ANDROID_SDK
5 stan* System Trace Number - A unique number generated per request by the client. It is used to uniquely identify a request from a client. It can be used for troubleshooting. 98321892319
6 user_handle_type* Indicates the type of the user identifier, who has initiated the request. Eg: UUID, MSISDN, EMAIL. If not relevant, "DEFAULT" can also be used. EMAIL
7 user_handle_value* Value of user_handle_type.If not relevant, "DEFAULT" can also be used. a@b.com/DEFAULT
8 location Specifies the geo location of the device that initiated the request.
9 transmission_datetime* Time in System Milli Seconds at which the request was initiated by the client. 155325508029
10 run_mode* TRIAL - Indicates need for mock run of the transaction. No transaction posting will happen. REAL - Indicates the actual transaction posting request. If not relevant, "DEFAULT" can also be used. DEFAULT/TRIAL/REAL/RETRY
11 client_ip IP Address of the client device.In case not applicable, an EMPTY ( "") string can be passed.
12 operation_mode* The way in which the request is executed. SELF - aadhaar holder doing the transaction. ASSISTED - where auth/ekyc is done in operator assisted mode. SYSTEM- where transaction is automated by system (batch processes). If not relevant, "DEFAULT" can also be used. DEFAULT/SELF/ASSISTED/SYSTEM
13 channel_version* The version of the channel application like ANDROID_SDK. 3.1.7
14 function_code* DEFAULT: Indicates default behavior for this API DEFAULT
15 function_sub_code DEFAULT: Indicates default behavior for this API DEFAULT
16 request_id* Client generated Id for referencing the transaction uniquely. requestid123
17 hash* Hash to be calculated by the client to verify the integrity of the request. Please refer APPENDIX B to see how to generate the hash.
18 api_key* api_key shared with the client during onboarding. samplekey123

Appendix B: Hash Generation

--> docInfoExtract : client_code|request_id|api_key|salt

--> faceExtract : client_code|request_id|api_key|salt

--> faceCompare : client_code|request_id|api_key|salt

Example (for _init request):
 If your 
         client_code=a1b2c3, 
         api_key=123,
         requestId=1234567890101112,
         salt=e1d2c3b4a,
 then 
         Hash-Sequence=a1b2c3|1234567890101112|123|e1d2c3b4a
         hash =SHA-256(Hash-Sequence)

Appendix C : Input Image Specification

For better results, it is recommended to ensure that Input Image complies to the following specifications :

Appendix D: Decryption Logic

To decrypt the response, please follow the steps below:

// byte array of the base64 decoded response
byte[] decodeResponse = <<base64_decode_response>>;

// open your p12 file
// to generate your p12 file, refer Appendix E
InputStream inputStream = new FileInputStream("<<your_p12_file>>.p12");

// your keystore credentials
char[] keystorePassword = "<<your_keystore_password>>".toCharArray();
char[] aliasPassword = "<<your_alias_password>>".toCharArray();
String alias = "<<your_alias>>";

KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(inputStream, keystorePassword);

// get the private key
PrivateKey privateKey =
                    (PrivateKey) keyStore.getKey(alias, aliasPassword);

ResponseByteArraySpliter splitter =
                    new ResponseByteArraySpliter(decodeResponse);

// get the symmetric key
byte[] sKey =
DecryptionUtil.decryptUsingPrivateKey(
privateKey, splitter.getEncryptedSymmetricKey()
);


// byte array of the decrypted data
byte[] decryptedData =
DecryptionUtil.decryptUsingSymmetricKey(
sKey, splitter.getEncryptedData()
);

// ResponseByteArraySplitter class
// Note: Please alter the access modifiers according to your packaging
// strategy.
public class ResponseByteArraySplitter {
        private static final int SYMMETRIC_KEY_SIZE = 256;

        private final byte[] encryptedSymmetricKey;
        private final byte[] encryptedData;
        public ResponseByteArraySplitter(byte[] data) throws Exception {
        int offset = 0;
        encryptedSymmetricKey = new byte[SYMMETRIC_KEY_SIZE];

        copyByteArray(
            data,
            offset,
            encryptedSymmetricKey.length,
            encryptedSymmetricKey
        );

        offset = offset + SYMMETRIC_KEY_SIZE;
        encryptedData = new byte[data.length - offset];
        copyByteArray(data, offset, encryptedData.length, encryptedData);
        }

        byte[] getEncryptedSymmetricKey() {
        return encryptedSymmetricKey;
        }

        byte[] getEncryptedData() {
        return encryptedData;
        }

        private void copyByteArray(byte[] src, int offset, int length,byte[] dest) throws Exception {

        try {
                System.arraycopy(src, offset, dest, 0, length);
            } catch (Exception e) {
              throw new Exception("Decryption failed, Corrupted packet!", e);
             }
        }
    }

    import java.io.IOException;
    import java.security.GeneralSecurityException;
    import java.security.PrivateKey;
    import javax.crypto.Cipher;
    import org.bouncycastle.crypto.InvalidCipherTextException;
    import org.bouncycastle.crypto.engines.AESEngine;
    import org.bouncycastle.crypto.paddings.PKCS7Padding;
    import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
    import org.bouncycastle.crypto.params.KeyParameter;

    public class DecryptionUtil {
    private static final String ASYMMETRIC_ALGO =
                                            "RSA/ECB/PKCS1Padding";

    public static byte[] decryptUsingPrivateKey(

    PrivateKeyprivateKey,
    byte[] data)
    throws IOException, GeneralSecurityException {
    Cipher pkCipher = Cipher.getInstance(ASYMMETRIC_ALGO);
    pkCipher.init(Cipher.DECRYPT_MODE, privateKey);
    return pkCipher.doFinal(data);
    }

    public static byte[] decryptUsingSymmetricKey(byte[] symmetricKey, byte[] data) throws InvalidCipherTextException {
    PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
                                                          new AESEngine(), new PKCS7Padding()
    );

    cipher.init(false, new KeyParameter(symmetricKey));
    int outputSize = cipher.getOutputSize(data.length);
    byte[] tempOP = new byte[outputSize];
    int processLen = cipher.processBytes(data,0,data.length,tempOP,0);
    int outputLen = cipher.doFinal(tempOP, processLen);
    byte[] result = new byte[processLen + outputLen];
    System.arraycopy(tempOP, 0, result, 0, result.length);
    return result;
  }
}

Appendix E: Certificates Generation for response encryption

How to generate a CSR?

​Steps to generate a CSR file using OpenSSL.

  1. Create private key with a validity

    • keytool -genkey -alias ALIAS_NAME -keyalg RSA -keystore COMP_NAME.jks
    • keysize 2048 -dname "CN=COMMON_NAME, EMAILADDRESS=sample@abc.com, C=IN,OU=ORG_UNIT, O=ORGANIZATION_NAME"
  2. Create CSR for above key

    • keytool -certreq -alias ALIAS_NAME -file COMP_NAME.csr -keystore COMP_NAME.jks
  3. Convert jks store to p12

    • keytool -importkeystore -srckeystore COMP_NAME.jks -destkeystore COMP_NAME.p12 -srcstoretype jks -deststoretype pkcs12
  4. Get private key from .p12 file

    • openssl pkcs12 -in COMP_NAME.p12 -nodes -out COMP_NAME.key -nocerts
  5. Self sign .csr file using the private key to get .cer file

    • openssl x509 -req -in COMP_NAME.csr -signkey COMP_NAME.key -out COMP_NAME.cer

Provide us the .cer file (public key certificate) generated above using which KYC response will be encrypted and use .p12 (private key) for decryption.

Glossary

Details to be ensured while generating the “CSR” through the terminal window by the customer are:

Appendix F : Error Codes

Error Code Scenario Message
380024 General user_id is mandatory
380025 General hash is mandatory
380026 General api_key is mandatory
380048 General Invalid api_key or client_code
380048 General Invalid api_key or client_code
380091 General Hash validation failed
380076 General No such user-id exists
333 General Unexpected Error Occurred
380135 faceExtract The face could not be extracted from the image.
380136 faceCompare image_1 is mandatory
380147 faceCompare image_2 is mandatory
380090 faceCompare Hash is mandatory
380124 docInfoExtract Ocr failed on the given document image.
380122 docInfoExtract Document type {} is not supported.
380123 docInfoExtract Could not extract the face from the document image.
380125 docInfoExtract The document could not be detected.

Appendix G : Important Things To consider before Uploading the Image