IDify Sessions (API)

The go-to solution for using Datakeen's screens in your application

Verification of Identity documents is essential for onboarding processes and KYC compliance. It allows you to confirm the identity of an individual, ensuring that they are who they claim to be. This is vital for preventing identity fraud and building trust with your customers.

As a developer looking to integrate Datakeen's screens in your application you can generate a IDify link that you can send to your end customers in order for them to capture their identity card and take a selfie. Results are available by API.

📘

Please note that contrary to Off the Shelf APIs the host URL for IDify sessions is https://app.datakeen.co


I. Create sessions by API

1. Retrieve a new token (POST)

In order to retrieve a token you must make a POST call on the /auth endpoint. The token is valid 24h.

POST Request

curl --location 'https://app.datakeen.co/backend/auth' \
--header 'Content-Type: application/json' \
--data '{
    "username": string,
    "password": string
}'
const axios = require('axios');
let data = JSON.stringify({
  "username": string,
  "password": string
});

let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: 'https://app.datakeen.co/backend/auth',
  headers: { 
    'Content-Type': 'application/json'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});

import http.client
import json

conn = http.client.HTTPSConnection("app.datakeen.co")
payload = json.dumps({
  "username": string,
  "password": string
})
headers = {
  'Content-Type': 'application/json'
}
conn.request("POST", "/backend/auth", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

POST Answer

The POST answer is the following :

{
    "status": 200,
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2M2RhNzU4NGVmNWViYTAwMTIzNDRjOTciLCJmdWxsTmFtZSI6IkFkbWluIE9ueSIsImVtYWlsIjoiYWRtaW5AZGF0YWtlZW4uY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzI2NzM1MzUzLCJleHAiOjE3MjY4MjE3NTN9.PcdqdPstUO9ByVhkTuYRidmB-m-44M25SKykKs-FmKQ"
    }
}

2. Retrieve session templates (GET)

If you have more than one session template for your customers. For example :

  • Template 1 : verify identity document and selfie
  • Template 2 : verify identity document and proof of address

You can retrieve template ids with the following call :

📘

You must add a header with Authorization: Basic {token} to be authorized

GET Request

curl --location 'https://app.datakeen.co/backend/admin/templates'
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {token}'
const axios = require('axios');

let config = {
  method: 'get',
  maxBodyLength: Infinity,
  url: 'https://app.datakeen.co/backend/api/v1/session/{sessionId}',
  headers: { }
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
import http.client

conn = http.client.HTTPSConnection("app.datakeen.co")
payload = ''
headers = {}
conn.request("GET", "/backend/api/v1/session/{sessionId}", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

GET Answer

[
    {
        "templateKey": "67516435254355c29507d255",     
        "name": "Vérification Identité avec selfie",
        "default": true,
        "status": "active",
        "_id": "6747253f954e810012b37zer",
        "createdAt": "2024-11-27T13:57:19.752Z"
    },
    {
        "templateKey": "67516435254355c29507d266",
        "name": "Vérification Identité et Justificatif de domicile",
        "default": false,
        "status": "active",
        "_id": "6745fcdf34177200112cc345",
        "createdAt": "2024-11-26T16:52:47.731Z"
    }
]

The following fields are returned

  • templateKey : this field is a fix id of the library template, it does not vary in time
  • _id : this field is the unique id of the template, it can vary if there is a new version
  • default : is this template the default template true / false
  • status : "active" or "inactive"
  • createdAt : time of creation

3. Create a new session (POST)

A session corresponds to the verification of one client. Each session is single-use and comes with an expiration time of 1 hour. Once the expiration time has passed, you cannot perform ID verification using that session, and a new session must be created.

In order to retrieve a new session link (unique URL) you must make a POST call on the /session endpoint.

In option you can provide the following information : "templateId", "firstName", "lastName", "birthDate", "callbackURL", "webhookURL" and are detailed below :

POST Request

  • templateId [OPTIONAL] : STRING - the unique identifier of the template (see the get templates results)
  • templateKey [OPTIONAL] : STRING - the unique key of the template(see the get templates results)
  • firstName [OPTIONAL] : STRING - the first name of the person to verify
  • lastName [OPTIONAL] : STRING - the last name of the person to verify
  • birthDate [OPTIONAL] : STRING - the birth date of the person to verify ('dd/mm/yyyy' format)
  • callbackURL [OPTIONAL] : STRING - a URL to redirect the user when the session is over
  • webhookURL [OPTIONAL] : STRING -a URL to call when the session is over. The call made is a POST request with the following payload : {sessionId : "<session id>"}.
  • QR : BOOLEAN [OPTIONAL] - True/False. option to generate a QR Code. This QR Code redirects the user to the unique session URL.

Authentication

📘

You must add a header with Authorization: Basic {token} to be authorized

curl --location 'https://app.datakeen.co/backend/api/v1/session' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {token}' \
--data '{
    "QR": boolean,
    "firstName": "JEAN",
    "lastName": "DUPONT",
    "birthDate": "01/01/1970",
    "webhookURL": "https://google.com",
    "callbackURL": "https://google.com"
}'
const axios = require('axios');
let data = JSON.stringify({
  "QR": boolean,
  "firstName": `${firstname}`,
  "lastName": `${lastName}`
});

let config = {
  method: 'post',
  maxBodyLength: Infinity,
  url: 'https://app.datakeen.co/backend/api/v1/session',
  headers: { 
    'Content-Type': 'application/json', 
    'Authorization': 'Basic {token}'
  },
  data : data
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});

import http.client
import json

conn = http.client.HTTPSConnection("app.datakeen.co")
payload = json.dumps({
  "QR": True,
  "firstName": "alex",
  "lastName": "bdt"
})
headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Basic {token}'
}
conn.request("POST", "/backend/api/v1/session", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

POST Answer

{
    "status": 200,
    "message": "Magic Link",
    "data": {
        "link": "https://app.datakeen.co/client/test?sessionId=662f5f9c9c18e30012c2f021",
        "sessionId": "662f5f9c9c18e30012c2f021",
        "QRCode": "data:image/example"
    }
}

You will receive a link to access the session, along with a sessionId and a QR code encoded in base64 if set to true.


4. Get the result of an IDify session by sessionId (GET)

You can retrieved the result of a past session with it's id.

📘

You must add a header with Authorization: Basic {token} to be authorized

GET Request

curl --location 'https://app.datakeen.co/backend/api/v1/session/{sessionId}'
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {token}'
const axios = require('axios');

let config = {
  method: 'get',
  maxBodyLength: Infinity,
  url: 'https://app.datakeen.co/backend/api/v1/session/{sessionId}',
  headers: { }
};

axios.request(config)
.then((response) => {
  console.log(JSON.stringify(response.data));
})
.catch((error) => {
  console.log(error);
});
import http.client

conn = http.client.HTTPSConnection("app.datakeen.co")
payload = ''
headers = {}
conn.request("GET", "/backend/api/v1/session/{sessionId}", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))

GET Answer

The list of codes are the following :

CodeCode NameDescription
1.0ai_approvedThe document has been fully verified by the AI, it is valid and conform
2.2readabilityThe document could not be processed: Could not read textual information
2.4missingMRZThe document could not be processed: The MRZ could not be found
2.5missingPartThe document could not be processed: One side of the document is missing
2.6noIdFoundThe document could not be processed: Could not find any ID
3.0expiredThe document has expired
4.0toVerifyWe could not process all the controls on the given document, please review its authenticity
5.0suspiciousAt least one element on the document is suspected to be fraudulent
7.0ownerThe owner of the document does not match the user input
8.0specimenThe document has been found in our specimen database

You will find below an example of an API answer :

{
    "id": "67eeb0948e593a00113f0963",
    "code": "1",
    "status": "ai_approved",
    "entities": [
        {
            "id": "67eeb0a68e593a00113f0980",
            "code": "1",
            "status": "ai_approved",
            "firstName": "JEAN",
            "lastName": "DUPONT",
            "birthDate": ""
        }
    ],
    "documents": [
        {
            "id": "67eeb0f48e593a00113f09a7",
            "code": "1.0",
            "type": "national_id_card_fr_2003",
            "status": "ai_approved",
            "extractedInformations": {
                "address": {
                    "confidence": 0.43034044685720474,
                    "value": "1 BOULEVARD DES CAPUCINES 750002 PARIS"
                },
                "birthCountry": {
                    "confidence": 0.9,
                    "value": "FR"
                },
                "birthDate": {
                    "confidence": 0.9017348030120296,
                    "value": "25.09.1990"
                },
                "birthDepartment": {
                    "confidence": 0.9,
                    "value": "75"
                },
                "birthPlace": {
                    "confidence": 0.8898096682009218,
                    "value": "PARIS 20E ARRONDISSEMENT"
                },
                "countryCode": {
                    "confidence": 0.9,
                    "value": "FR"
                },
                "deliveryDate": {
                    "confidence": 0.8680884299127136,
                    "value": "01.04.2021"
                },
                "deliveryDateMRZ": {
                    "confidence": 1,
                    "value": "04.2021"
                },
                "deliveryDepartmentMRZ": {
                    "confidence": 1,
                    "value": "94"
                },
                "expiryDate": {
                    "confidence": 0.8898178831756276,
                    "value": "31.03.2036"
                },
                "firstName": {
                    "confidence": 0.8196075294754301,
                    "value": "JEAN"
                },
                "firstName1": {
                    "confidence": 0.8196075294754301,
                    "value": "JEAN"
                },
                "firstName2": {
                    "confidence": null,
                    "value": ""
                },
                "firstName3": {
                    "confidence": null,
                    "value": ""
                },
                "firstNameMRZ": {
                    "confidence": 1,
                    "value": "DUPONT"
                },
                "fullName": {
                    "confidence": 0.7701102725721481,
                    "value": "JEAN DUPONT"
                },
                "gender": {
                    "confidence": 0.892943529614854,
                    "value": "M"
                },
                "genderMRZ": {
                    "confidence": 1,
                    "value": "M"
                },
                "idNumber": {
                    "confidence": 0.9587318960040534,
                    "value": "210494150111"
                },
                "idNumberMRZ": {
                    "confidence": 1,
                    "value": "210494150111"
                },
                "lastName": {
                    "confidence": 0.9396085868867488,
                    "value": "DUPONT"
                },
                "lastNameMRZ": {
                    "confidence": 1,
                    "value": "DUPONT"
                },
                "mrz": {
                    "confidence": 1,
                    "value":""
                },
                "nationality": {
                    "confidence": 0.9,
                    "value": "FR"
                },
                "nationalityMRZ": {
                    "confidence": 1,
                    "value": "FRA"
                },
                "spouseName": {
                    "confidence": null,
                    "value": ""
                }
            },
            "controlCategories": {
                "dataCoherency": {
                    "controls": {
                        "countryCodeConformity": {
                            "confidence": 1,
                            "value": true
                        },
                        "durationValidity": {
                            "confidence": 0.8789531565441706,
                            "value": true
                        },
                        "matchDeliveryDateMRZ": {
                            "confidence": 0.9,
                            "value": true
                        },
                        "matchFirstNameMRZ": {
                            "confidence": 0.95,
                            "value": true
                        },
                        "matchLastNameMRZ": {
                            "confidence": 0.95,
                            "value": true
                        },
                        "matchNationalityMRZ": {
                            "confidence": 1,
                            "value": true
                        },
                        "mrzConformity": {
                            "confidence": 1,
                            "value": true
                        },
                        "notExpired": {
                            "confidence": 0.8898178831756276,
                            "value": true
                        },
                        "notSpecimen": {
                            "confidence": 1,
                            "value": true
                        }
                    },
                    "status": true
                },
                "input": {
                    "controls": {
                        "matchFirstName": {
                            "confidence": 0.8,
                            "value": true
                        },
                        "matchFullName": {
                            "confidence": 0.8,
                            "value": true
                        },
                        "matchLastName": {
                            "confidence": 0.8,
                            "value": true
                        }
                    },
                    "status": true
                },
                "metadata": {
                    "controls": {
                        "notMultipleVersions": {
                            "confidence": null,
                            "value": null
                        },
                        "notSuspectedSoftware": {
                            "confidence": null,
                            "value": null
                        }
                    },
                    "status": null
                },
                "visual": {
                    "controls": {
                        "initialsIsPresent": {
                            "confidence": 0.7879152297973633,
                            "value": true
                        },
                        "mrzVisualConformity": {
                            "confidence": 0.8,
                            "value": true
                        },
                        "photoIsPresent": {
                            "confidence": 0.9749646186828613,
                            "value": true
                        },
                        "rfSymbolIsPresent": {
                            "confidence": 0.2181098908185959,
                            "value": true
                        },
                        "stampIsPresent": {
                            "confidence": 0.928052544593811,
                            "value": true
                        },
                        "waveIsPresent": {
                            "confidence": 0.8638243675231934,
                            "value": true
                        }
                    },
                    "status": true
                }
            },
            "metadata": [
                {
                    "author": null,
                    "created_date": null,
                    "creator": null,
                    "file": "idcard_0.png",
                    "keywords": null,
                    "modified_date": null,
                    "producer": null,
                    "subject": null,
                    "title": null
                },
                {
                    "author": null,
                    "created_date": null,
                    "creator": null,
                    "file": "idcard_1.png",
                    "keywords": null,
                    "modified_date": null,
                    "producer": null,
                    "subject": null,
                    "title": null
                }
            ]
        },
        {
            "id": "67eeb1108e593a00113f09bf",
            "code": "1.0",
            "type": "photo",
            "status": "ai_approved",
            "extractedInformations": {},
            "controlCategories": {},
            "metadata": {}
        }
    ]
}

II. Common integrations into mobile applications

React Native

Here is an example of an integration of the session in React Native

For each client generate a session thanks to the create a new session API call.

Redirect the client to the given session in a web view, like so :

import WebView from "react-native-webview";

export function DatakeenScreen() {
  return (
    <WebView
      allowsInlineMediaPlayback
      source={{
        uri: "https://staging.datakeen.co/client/test?sessionId=6634a788316f3d00116c4378",
      }}
    />
  );
}

At the end of the session a webhook is called.

Flutter

Here is an example of an integration of the session in Flutter

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

Widget buildWebView() {
  return WebView(
    initialUrl: 'https://staging.datakeen.co/client/test?sessionId=6634abb6316f3d00116c4578',
    javascriptMode: JavascriptMode.unrestricted,
  );
}

5. Get a document from a specific session (GET)

You can retrieve a document associated with a session by using the document ID and session ID.

📘

You must add a header with Authorization: Basic {token} to be authorized

GET Request

curl --location 'https://app.datakeen.co/backend/api/v1/session/{sessionId}/document/{documentId}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic {token}'
const axios = require("axios");

const getDocument = async (sessionId, documentId, token) => {
  try {
    const response = await axios.get(
      `https://app.datakeen.co/backend/api/v1/session/${sessionId}/document/${documentId}`,
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Basic ${token}`,
        },
      }
    );
    return response.data;
  } catch (error) {
    console.error("Error fetching document:", error);
    throw error;
  }
};
import requests

def get_document(session_id, document_id, token):
    url = f"https://app.datakeen.co/backend/api/v1/session/{session_id}/document/{document_id}"
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Basic {token}"
    }
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()

GET Answer

You will receive a JSON response containing the document data in base64 format:

{
  "status": 1,
  "message": "Document found",
  "data": {
    "documentBase64": "base64EncodedString...",
    "fileName": "document_filename.jpg",
    "allFilesData": [
      {
        "base64": "base64EncodedString...",
        "name": "document_filename.jpg"
      }
    ]
  }
}

Response Fields

FieldDescription
documentBase64The base64-encoded content of the document, which you can decode to view or save the original document
fileNameThe original filename of the document
allFilesDataAn array containing all file data related to this document

Possible Error Responses

Status CodeDescription
401Unauthorized - Invalid or missing authentication token
404Not Found - Session or document not found
500Server Error - Error retrieving document from storage