Skip to main content

Tracking Progress

When creating the flow using the call to our API, the response of that call contains a token. You can use this token to track progress and to fetch (validated) data from the API. The response looks as follows:

{
"checkToken": "abcdef",
"readyForProfessional": false
}

The token should be saved on your side, in order to use it later on to query the progress. The readyForProfessional indicates whether the flow can be started by the professional. If it is false, please wait for the READY callback before providing the professional with the link to the screening.

Retrieving Flow Status​

You can use the token and the following endpoint to query the progress and (validated) data:

GET https://[api-url]/validation-flow/check/[token]
Accept: application/json
Authorization: Bearer YOUR_API_KEY
Authorization-Method: api-token

Complete Response Structure​

You'll get back the following structure:

{
"id": 123,
"dateCreated": "2023-12-18T13:44:25.471306Z",
"dateModified": "2023-12-18T15:57:27.505264Z",
"recruiterFirstName": "Test",
"recruiterLastName": "Kees",
"recruiterEmail": "test.kees@otentica.nl",
"recruiterCompanyName": "Test",
"professionalFirstName": "Kandidaat",
"professionalLastName": "Test",
"professionalEmail": "kandidaat.test@domain.tld",
"professionalPhone": "0612345678",
"status": "ALL_STEPS_DONE",
"canFinish": false,
"finished": true,
"checks": [
{
"checkIdentifier": "identity-data",
"status": "SUCCESS",
"contentType": "application/vnd.otentica.singlecheck.check.identity-data+json",
"importance": "SKIPPABLE",
"fields": [],
"results": [
{
"group": null,
"checkName": "identity-data",
"fieldName": "lastName",
"value": "Test",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.713299Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "initials",
"value": "T",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.674538Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "firstName",
"value": null,
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.689345Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "lastNamePrefix",
"value": null,
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.699336Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "dateOfBirth",
"value": "1970-01-01",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.724222Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "gender",
"value": "MALE",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.734124Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "street",
"value": "Straat",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.744029Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "houseNumber",
"value": "1",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.753413Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "houseNumberSuffix",
"value": null,
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.762992Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "postalCode",
"value": "1234AB",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.772308Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "city",
"value": "Stad",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.781400Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "countryCode",
"value": "NL",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.790334Z"
}
],
"configuration": null,
"completedAsExpected": true
},
{
"checkIdentifier": "correspondence-data",
"status": "SUCCESS",
"contentType": "application/vnd.otentica.singlecheck.check.correspondence-data+json",
"importance": "NOT_SKIPPABLE",
"fields": [],
"results": [
{
"group": "naw",
"checkName": "correspondence-data",
"fieldName": "email",
"value": "test@kees.nl",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "correspondence-data",
"fieldName": "phone",
"value": "0612345678",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
}
],
"configuration": null,
"completedAsExpected": true
},
{
"checkIdentifier": "identity-document-v2",
"status": "SUCCESS",
"contentType": "application/vnd.otentica.singlecheck.check.identity-document-v2+json",
"importance": "SKIPPABLE",
"fields": [],
"results": [
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "identityDocument",
"value": "e7e5e957-f010-4de7-9423-fe7581965192",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "identityDocumentBackside",
"value": "31a2bda0-3722-458c-b84d-1a8316863a5f",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "selfie",
"value": "02ba468a-b481-4e30-ac6c-b0ce9af9996a",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "identityDocumentType",
"value": "PASSPORT",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "identityDocumentNumber",
"value": "AB123456",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "personalNumber",
"value": "123456789",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "nationality",
"value": "NLD",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
},
{
"group": null,
"checkName": "identity-document-v2",
"fieldName": "identityDocumentDateOfExpiry",
"value": "2030-01-01",
"verified": false,
"verifiedBy": null,
"verifiedOn": null
}
],
"configuration": {
"applicantLevelName": "id-and-liveness"
},
"completedAsExpected": false
},
{
"checkIdentifier": "unverified-diploma-data",
"status": "SKIPPED",
"contentType": "application/vnd.otentica.singlecheck.check.unverified-diploma-data+json",
"importance": "SKIPPABLE",
"fields": [],
"results": [],
"configuration": null,
"completedAsExpected": false
},
{
"checkIdentifier": "diploma-data",
"status": "SUCCESS",
"contentType": "application/vnd.otentica.singlecheck.check.diploma-data+json",
"importance": "SKIPPABLE",
"fields": [],
"results": [
{
"group": null,
"checkName": "diploma-data",
"fieldName": "diplomas",
"value": [
{
"type": "DIPLOMA",
"validFrom": null,
"validUntil": null,
"title": null,
"description": "IT Bachelor",
"issuer": "Hogeschool Utrecht",
"level": "0",
"verifiedOn": "2023-12-18T15:56:52.373384392Z",
"verifier": "duo",
"verificationStatus": null,
"fileId": "9caf3ebd-460b-4583-9823-1e9309289192"
}
],
"verified": true,
"verifiedBy": "emrex",
"verifiedOn": "2023-12-18T15:56:52.402431Z"
}
],
"configuration": {
"onlySkippableIfUnverifiedDiplomaAdded":true
},
"completedAsExpected": true
},
{
"checkIdentifier": "bank-data",
"status": "SUCCESS",
"contentType": "application/vnd.otentica.singlecheck.check.bank-data+json",
"importance": "SKIPPABLE",
"fields": [],
"results": [
{
"group": null,
"checkName": "bank-data",
"fieldName": "personalIbanCountry",
"value": "NL",
"verified": true,
"verifiedBy": "otentica",
"verifiedOn": "2023-12-18T15:57:28.011815Z"
},
{
"group": null,
"checkName": "bank-data",
"fieldName": "personalIban",
"value": "NL00RABO123455677",
"verified": true,
"verifiedBy": "otentica",
"verifiedOn": "2023-12-18T15:57:27.995607Z"
},
{
"group": null,
"checkName": "bank-data",
"fieldName": "personalIbanName",
"value": "T Kees",
"verified": true,
"verifiedBy": "otentica",
"verifiedOn": "2023-12-18T15:57:28.003952Z"
}
],
"configuration": {
"checkPersonalAccount": true,
"checkBusinessAccount": false
},
"completedAsExpected": true
}
],
"results": [
{
"group": null,
"checkName": "bank-data",
"fieldName": "personalIbanCountry",
"value": "NL",
"verified": true,
"verifiedBy": "otentica",
"verifiedOn": "2023-12-18T15:57:28.011815Z"
},
{
"group": null,
"checkName": "bank-data",
"fieldName": "personalIban",
"value": "NL00RABO123455677",
"verified": true,
"verifiedBy": "otentica",
"verifiedOn": "2023-12-18T15:57:27.995607Z"
},
{
"group": null,
"checkName": "identity-data",
"fieldName": "lastName",
"value": "Kees",
"verified": true,
"verifiedBy": "idin",
"verifiedOn": "2023-12-18T15:50:43.713299Z"
},
[...]
],
"style": {
"logoUrl": "https://app.otentica.nl/logos/logo.png",
"primaryColor": "#000000",
"secondaryColor": "#111111",
"primaryButtonColor": "#222222"
},
"expiryDate": "2024-02-01",
"anonymized": false,
"anonymizedOn": null,
"readyForProfessional": true,
"termsAndConditionsAccepted": true,
"logoutRedirectUrl": null,
"personalizedMessage": "<p>Welcome to our screening process.</p>",
"language": "DUTCH"
}

Important Fields​

Take note of the following interesting fields:

Check Status Values​

Status as part of a check:

  • PENDING: The check is not done yet
  • SUCCESS: The check is completed (but not necessarily a successful validation)
  • SKIPPED: The check is skipped
  • FAILED: The check failed. This rarely happens, and usually occurs when there is a third party error
  • ASYNC_WAITING: The check is waiting for an external party (for example the referents)

completedAsExpected​

This indicates if the check was completed as expected. If this is false, manual investigation is advised. If this is true, the data was verified (except for some checks that do not validate data such as the unverified certificates check).

Data Retrieval Best Practices​

We advise to retrieve the data from the results field. Sometimes data is retrieved using different checks to maximize the data availability. Also note that not all properties may return a value. For example using iDIN, some banks only return the initials of the name and not the complete first names. We try to retrieve as much data as possible, but we are dependent on the data provided by the third parties.

important

After the expiry date of the screening, the data is anonymized and thus no longer available.

Response Fields​

Top-Level Fields​

FieldTypeDescription
idintegerFlow ID
dateCreateddatetimeWhen the flow was created
dateModifieddatetimeLast modification time
statusstringCurrent flow status
finishedbooleanWhether all checks are complete
canFinishbooleanWhether the flow can be marked as finished
checksarrayList of all checks and their results
resultsarrayAggregated results from all checks
expiryDatedateWhen the screening data will be anonymized
anonymizedbooleanWhether data has been anonymized
anonymizedOndatetimeWhen the data was anonymized (null if not yet)
readyForProfessionalbooleanWhether the flow is ready for the professional
termsAndConditionsAcceptedbooleanWhether the professional accepted the T&C
nextCheckTypestring/nullNext check awaiting action (null if none)
languagestringLanguage of the flow (DUTCH or ENGLISH)
logoutRedirectUrlstring/nullURL to redirect after logout (if configured)
personalizedMessagestring/nullCustom HTML message shown to the professional
customQuestionsarrayThe screening's custom questions (see below)
customQuestionsAnsweredbooleanWhether the custom questions have been answered
customQuestionsAnalysisPendingbooleanWhether document analysis on an answer is still running
customQuestionsCompletedAsExpectedboolean/nullWhether the answers were as expected (null when not applicable)

Custom Question Object​

Each entry in customQuestions describes one question shown to the candidate. Questions are configured on a template and reach a screening through templateId.

FieldTypeDescription
idintegerThe ID referenced by customQuestionId in the webhook payload
labelstringThe question text as the candidate sees it
helpTextstring/nullOptional explanation shown below the question
questionTypestringOPEN_TEXT / YES_NO / SINGLE_CHOICE / MULTI_CHOICE / DATE / CONSENT
optionsstring/nullJSON array of choices, as a string. null for OPEN_TEXT and YES_NO
requiredbooleanWhether an answer is mandatory
sortOrderintegerDisplay order
templateIdinteger/nullThe template this question came from
parentQuestionIdinteger/nullnull for a top-level question; the parent's id for a sub-question
triggerValuestring/nullThe parent answer that reveals this sub-question, e.g. "yes". Only set on a sub-question
validityMonthsinteger/nullHow many months a DATE answer stays valid. This is what validUntil and validityStatus in the webhook are derived from
expectedAnswerstring/nullThe answer considered acceptable, where configured
documentUploadEnabledbooleanWhether a supporting document can be uploaded
documentUploadRequiredbooleanWhether that upload is mandatory
documentUploadPromptstring/nullPrompt shown for the upload
documentUploadTriggerModestringSPECIFIC_ANSWER or ANY_ANSWER
documentUploadTriggerValuestring/nullThe answer that triggers the upload request
documentAiAnalysisEnabledbooleanWhether the uploaded document is read automatically
verifyAnswerWithDocumentbooleanWhether the document is checked against the answer, producing verdict in the webhook
Sub-questions are flat here, nested in the webhook

In customQuestions a sub-question is a sibling of its parent, identified by parentQuestionId. In the webhook's customAnswers the corresponding answer is nested under its parent in subAnswers. Match the two by customQuestions[].id against customAnswers[].customQuestionId, and store label + questionType as your stable key — see Custom Question Answers.

Check Object​

Each check in the checks array contains:

FieldTypeDescription
checkIdentifierstringCheck type identifier
statusstringCheck status
contentTypestringCheck content type
importancestringSKIPPABLE or NOT_SKIPPABLE
fieldsarrayInput fields (if not completed)
resultsarrayVerified data (if completed)
completedAsExpectedbooleanWhether check completed successfully
configurationobjectCheck-specific configuration

Result Object​

Each result in the results array contains:

FieldTypeDescription
groupstring/nullResult grouping (if applicable)
checkNamestringCheck identifier
fieldNamestringField identifier
valueanyThe actual value
verifiedbooleanWhether the value is verified
verifiedBystringVerification method (e.g., "idin", "duo", "emrex")
verifiedOndatetimeWhen verification occurred

Polling for Updates​

If you're not using webhooks, poll this endpoint periodically:

async function pollFlowStatus(token) {
const response = await fetch(
`https://api.otentica.nl/validation-flow/check/${token}`,
{
headers: {
Authorization: `Bearer ${API_KEY}`,
"Authorization-Method": "api-token",
Accept: "application/json",
},
}
);

const data = await response.json();

if (data.finished) {
// Process completed flow
processResults(data.checks);
} else {
// Check again later
setTimeout(() => pollFlowStatus(token), 30000); // Poll every 30 seconds
}
}

Extracting Verified Data​

Filter and extract only verified data from the response:

function getVerifiedData(flowData) {
const verifiedData = {};

// Process data from the aggregated results field
flowData.results.forEach((result) => {
if (result.verified) {
verifiedData[result.fieldName] = {
value: result.value,
verifiedBy: result.verifiedBy,
verifiedOn: result.verifiedOn,
checkName: result.checkName,
};
}
});

return verifiedData;
}

// Example usage
const flowData = await getFlowStatus(checkToken);
const verifiedData = getVerifiedData(flowData);
console.log("Verified data:", verifiedData);

Monitoring Check Progress​

Track individual check completion:

function getCheckProgress(flowData) {
const total = flowData.checks.length;
const completed = flowData.checks.filter((check) =>
["SUCCESS", "FAILED", "SKIPPED"].includes(check.status)
).length;

return {
total,
completed,
percentage: Math.round((completed / total) * 100),
status: flowData.status,
};
}