NAV

Introduction

Welcome to the Veriff documentation. This documentation will help you get started with Veriff. If you have any questions you can use Intercom on the bottom of the page or support@veriff.com to get in contact with us.

Getting started with Veriff

Signing up

Fill-in the form at veriff.com and an e-mail with instructions on how to log in will be sent to you.

Logging in

Veriff’s environment can be accessed by going to Veriff Station. To log in for the first time, please use the link sent in the email and follow instructions.

New users for your colleagues can be created by an Administrator role account in Veriff Station Team page.

How to find your API keys

Your API keys are stored in the Veriff Station. Choose Integrations in the top menu, then integration you need.

Once you open integration you'll see Publishable key and Private key. Private key is your API secret and Publishable key is the API Key

There are two types of Integrations that can be created by Station user:

Sessions Lifecycle

Sessions Lifecycle

Generating verification with Veriff

Generating sessions manually

To generate a verification:

Generating sessions in code

{
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith"
        },
        "document": {
            "type": "PASSPORT",
            "country": "EE"
        },
        "vendorData": "unique id of a user",
        "timestamp": "2016-05-19T08:30:25.597Z"
    }
}

The simplest way to get a verification link for web verification flow is to create JSON object containing the user's name and the redirect(callback) URL to which the user will be sent after completing web verification flow (usually it is "Thank you" or "Waiting" page on your side). Then use HTTP POST to send the object to https://<Base-URL>/v1/sessions, with Content-Type application/json and the X-AUTH-CLIENT header containing your API Key.

In response, a JSON session ID and a URL will be sent as follows:

{
    "status": "success",
    "verification":{
        "id":"f04bdb47-d3be-4b28-b028-............",
        "url": "https://alchemy.veriff.com/v/sample-url.................",
        "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJh............",
        "baseUrl": "https://alchemy.veriff.com"
    }
}

The URL of the verification session is where end-user should be redirected on web.

The session ID should be saved on your end - you can tie back the webhook responses to your customer record that way.

The session token is a JSON web token that consists of HS256 encrypted session ID and session creation timestamp. It will expire in 7 days.

Once the end user completes the verification flow, they will be redirected to the callback URL. If callback URL is not specified for the session, user will be redirected to Integration's default Callback URL, which can be set up in Station. It is important to note, the callback does not contain any decision or verification information yet.

Receiving decisions

There are 3 options to fetch decisions for a verification session:

Signing requests with the X-HMAC-SIGNATURE

The X-HMAC-SIGNATURE guarantees to us, that API calls are initiated by you. It is based on the shared key (API private key) that is known only by you and Veriff.

X-HMAC-SIGNATURE is a special header which value is a signature generated using HMAC method.

To generate the value you need to generate a keyed hash value using your API private key with the HMAC-SHA256 function.

Try our demos

Web

To try out the end-user facing verification flow, navigate to Veriff Demo - this will start the verification flow in your browser.

Mobile SDKs

Our mobile applications can be tested by downloading the demo versions: iOS / Android

Review Verifications

To review the submitted verification and its results, visit Veriff Station and View the individual verification sessions.

Supported Browsers for verification flow

Desktop

In addition to the listed browsers other Chromium based browsers may work.

iOS

Android

In addition to the listed browsers other Chromium based browsers may work.

Not supported browsers

A list of browsers which are not supported currently and that we can not support unless there are browser side changes implemented by the browser vendor:

Android

iOS

Integrations

Before you can launch the verification flow you'll need a sessionUrl.
See our documentation on generating one. sessionUrl should be unique for each call.

Android SDK integration

Android SDK Requirements

Please check the migration guides below if you need any guidance for updating these dependencies.

Adding the Android SDK

Open the root build.gradle file and add a new maven destination to the repositories in the allprojects section.

allprojects {
    repositories {
        maven { url "https://cdn.veriff.me/android/" } //make sure to add this before other repos
        google()
        jcenter()
    }
}

Add the Veriff SDK dependency to the application build.gradle file:

implementation 'com.veriff:veriff-library:4.+'

Permissions

The SDK will request all the permissions it needs, please make sure that the CAMERA, RECORD_AUDIO permissions are not explicitly removed using tools:node="remove" in your app`s manifest file. Ignore this if you are not explicitly removing any permissions.

Starting the verification flow

The verification flow must be launched from the vendor Activity class with a unique session. A session is valid for 7 days and expires automatically after that.

import com.veriff.Sdk;

Intent intent = Sdk.createLaunchIntent(activity, sessionUrl);
startActivityForResult(intent, REQUEST_CODE);

Parameters are defined as below;

Parameters Description
sessionUrl Required parameter. sessionUrl can be received from your backend implementation. sessionUrl should be unique for each call. Check /sessions endpoint in the API documentation here to learn how to generate one.
REQUEST_CODE Define an integer constant REQUEST_CODE in your activity which can be used to compare in onActivityResult

Customizing the SDK

Setting a theme (Optional)

You can customize the look and feel of the SDK flow by passing a Branding object via Configuration to createLaunchIntent as shown in the example below.

Branding branding = new Branding.Builder()
        .themeColor(getResources().getColor(R.color.theme_color))
        .backgroundColor(getResources().getColor(R.color.background_color))
        .statusBarColor(getResources().getColor(R.color.status_bar_color))
        .primaryTextColor(getResources().getColor(R.color.primary_text_color))
        .secondaryTextColor(getResources().getColor(R.color.secondary_text_color))
        .toolbarIcon(R.drawable.toolbar_icon)
        .bulletPoint(R.drawable.bullet_point)
        .buttonCornerRadius(48f)
        .notificationIcon(R.drawable.notification_icon)
        .build();

Configuration configuration = new Configuration.Builder()
        .branding(branding)
        .build();


Intent intent = Sdk.createLaunchIntent(activity, sessionUrl, configuration);
startActivityForResult(intent, REQUEST_CODE);

All custom values for branding are optional. If a value is not provided for them the default Veriff color or icon will be used.

More customization options

Customizing primary button background color

You can customize the background color of the primary button in the SDK by passing a color value to the primaryButtonBackgroundColor method ot the Branding builder. Note that this value is expected to be a @ColorInt representation of color. In case the custom button background color is not specified the SDK will use themeColor. If themeColor is not specified SDK will use default color instead.

Branding branding = new Branding.Builder()
        .primaryButtonBackgroundColor(getResources().getColor(R.color.your_custom_button_background_color))
        .build();

Customizing the button height

You can customize the height of the buttons used in the SDK by passing a height value to the buttonHeight method of the Branding builder. Note that this value is expected to be a float denoting the dp value and should be in the inclusive range of 32f to 96f. In case the height is not customized or the value passed is out of the range the default height of 60dp will be used.

branding.buttonHeight(50f);

Customizing the font

You can customize the fonts used in the SDK by passing the resource IDs of the fonts you want to use. Make sure that you have added the font files to the font folder in your app. A custom font can be set by passing a com.veriff.Font object to the font method of Branding builder. The com.veriff.Font builder accepts 4 types of fonts via the setNormalAndBold setItalic and setBoldItalic methods. It is mandatory to pass both normal and bold fonts when setting a custom font hence the setNormalAndBold method accepts both, in case you want to set one type of font only you can pass the same font resource ID as arguments to both parameters to the setNormalAndBold method. The setItalic and setBoldItalic are optional and does not have any effect now, they are added for future compatibility.

branding.font(
    new com.veriff.Font.Builder()
    .setNormalAndBold(R.font.comic_neue_regular, R.font.comic_neue_bold)
    .build()
    );

Custom intro screen

Veriff supports replacing introduction screen with a custom client developed introduction screen for eligible clients. First, please ask about this possibility from your account manager. In case we can offer it for you then removal process is following:

Configuration configuration = new Configuration.Builder()
        .customIntroScreen(true)
        .build();

Note: Adding the configuration alone in your app is not enough to enable the custom intro screen. Make sure to contact your account manager so they can enable the feature for your integration.

Setting a locale for the SDK (Optional)

You can set a locale(java.util.Locale) for the SDK from the app itself.

Locale appLocale = Locale.ENGLISH;
Configuration configuration = new Configuration.Builder()
        .locale(appLocale)
        .build();

Intent intent = Sdk.createLaunchIntent(activity, sessionUrl, configuration);
startActivityForResult(intent, REQUEST_CODE);        

Getting the verification status

Veriff SDK sends callbacks to your mobile application. To capture the result override the onActivityResult method in your activity that started the verification flow:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE) {
        Result result = Result.fromResultIntent(data);
        if (result != null) {
            handleResult(result); //see below for handling the result
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

public void handleResult(Result result) {
    switch (result.getStatus()) {
    case DONE:
        // Session is completed from user's perspective
        break;
    case CANCELED:
        // User cancelled the session
        break;
    case ERROR:
        // An error occurred during the flow, Veriff has already shown UI, no need to display
        // a separate error message here
        Log.w(TAG, "Verification error occurred: " + result.getError());
        break;
    }
}

Adding error logging

To turn on logging, simply add your logging implementation instance (instance of com.veriff.Logger class) to the SDK before launching the SDK as shown.

Sdk.setLogger(<Instance of your logging class>);
Intent intent = Sdk.createLaunchIntent(activity, sessionUrl);
startActivityForResult(intent, REQUEST_CODE);

Excluding ML Kit support

Veriff Android SDK uses ML Kit for things like face detection, barcode scanning or text recognition to improve the user experience. It uses on-device models that are pre-downloaded by play services. If your app cannot use play services at all then you can exclude the transitive mlkit dependency - this will remove any dependencies on ML Kit and disable the use of these ML modules at runtime:

implementation('com.veriff:veriff-library:4.0.0') {
    exclude group: 'com.veriff', module: 'mlkit'
}

Android SDK Changelog

Please refer to release notes.

Migrating Veriff Android SDK from 3.x.x to 4.0.0

Follow these steps to migrate from SDK 3.x.x to 4.0.0

Android Gradle Plugin Open the root build.gradle file and change the classpath dependency in the buldscript section if that needed. java buildscript { repositories { ... } dependencies { classpath 'com.android.tools.build:gradle:3.4.1' // and above ... } }

Kotlin Open the root build.gradle file and change the classpath dependency in the buldscript section if that needed.

buildscript {
    ext.kotlinVersion = '1.4.0' // and above
    repositories {
      ...
    }
    dependencies {
        ...
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
        ...
    }
}

Proguard Open the root build.gradle file and add the force update to the buildscript section if that needed. java buildscript { ... configurations.all { resolutionStrategy { force 'net.sf.proguard:proguard-gradle:6.2.2' } } }

SDK integration Nothing changed in SDK except the public API types, please update the imports to java import com.veriff.Branding; import com.veriff.Configuration; import com.veriff.Font; import com.veriff.Result; import com.veriff.Sdk; and change types from VeriffBranding to Branding VeriffConfiguration to Configuration VeriffFont to Font VeriffResult to Result

Migrating Veriff Android SDK from 2.x.x to 3.0.0

Follow these steps to migrate from SDK 2.x.x to 3.0.0

Switch to AndroidX

Veriff SDK 3.0.0 requires AndroidX 1.0.0 or later. If you haven't switched to AndroidX in your app yet then follow this guide.

Enable Java 8

Veriff SDK 3.0.0 requires Java 8 language features to be enabled in your project. If you don't have this enabled already then add this to your app/build.gradle file under the android {} section:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

See more here.

Switch from baseUrl and sessionToken to sessionUrl

The new 3.0.0 SDK requires a single sessionUrl parameter instead of baseUrl and sessionToken. See the documentation here. As a backwards compatibility measure, if a sessionToken value is passed into the sessionUrl parameter then it will still work with an assumed baseUrl of magic.veriff.com.

Use com.veriff.* classes instead of mobi.lab.veriff.data.* ones

The name and location of the main SDK entry class has changed from mobi.lab.veriff.data.Veriff to com.veriff.VeriffSdk. The API is largely similar - instead of Veriff.Builder there's a VeriffSdk.createLaunchIntent method that returns an Intent which you can then use to launch veriff. See example here.

If you are using Branding to customize the look and feel of the SDK then it has a new name - VeriffBranding. The builder interface has been streamlined by removing the set* prefixes from all the methods. Read more about customization here.

Use com.veriff.VeriffResult instead of reading return Intent directly

Starting with 3.0.0 there's a new way to handle the result of the verification flow. Instead of reading INTENT_EXTRA_STATUS directly from the returned data intent, use VeriffResult.fromResultIntent(data) to get a result object with a status field and an optional error field. We've reduced status to just three - CANCELED, ERROR, DONE. In case of ERROR the error field contains more information. See the example here.

Remove usage of deprecated types

While the old SDK entrypoints are still present for backwards compatibility they will be removed in the future. Please remove usage of any SDK type marked with @Deprecated - the easiest way to discover these is to look at your Gradle build log with Java/Kotlin compilation warnings turned on.

Here's a list of old deprecated classes due to be removed in a future release:

mobi.lab.veriff.data.Veriff
mobi.lab.veriff.data.Veriff.Builder
mobi.lab.veriff.data.VeriffConstants
mobi.lab.veriff.data.Branding
mobi.lab.veriff.data.Branding.Builder
mobi.lab.veriff.data.DrawableProvider
mobi.lab.veriff.util.LogAccess
mobi.lab.veriff.util.LogAccess.LogLevel
mobi.lab.veriff.util.LogcatLogAccess
mobi.lab.veriff.service.VeriffStatusUpdatesService

iOS SDK integration

iOS SDK Requirements

Integration Veriff iOS SDK requires at least iOS version 9.0

Adding the SDK to the project

VeriffSDK requires the latest stable version of Xcode available at the time the release is made. If you would like to use versions that are independent of Swift versions, please integrate .xcframework.

Using Swift Package Manager

To integrate Veriff SDK with Swift Package Manager, open File > Swift Packages > Add Package Dependency and add the Veriff iOS SDK Swift Package repository url as stated below;

https://github.com/Veriff/veriff-ios-spm/

Using Cocoapods

To integrate Veriff SDK with Cocoapods, please add the line below to your Podfile and run pod install. New to Cocoapods? Learn more here.

pod 'VeriffSDK'

After installation is done, use the newly created .xcworkspace file of your project.

Using Carthage

To integrate VeriffSDK into your Xcode project using Carthage, specify the required libraries below in your Cartfile.

binary "https://cdn.veriff.me/ios/carthage/VeriffSDK.json"

Run carthage update --use-xcframeworks and then drag and drop Veriff.xcframework from Carthage/Build folder to the Frameworks, Libraries, and Embedded Content section on your app target's General settings tab.

Using XCFramework

To integrate VeriffSDK into your project manually, please download VeriffSDK.

After framework is downloaded, drag and drop Veriff.xcframework from Carthage/Build folder to the Frameworks, Libraries, and Embedded Content section on your app target's General settings tab.

Adding permissions

Add usage descriptions to application Info.plist

Not adding these usage descriptions causes system to kill application when it requests the permissions when needed.

Veriff iOS SDK requires camera, microphone, photo library and NFC reader permissions for capturing photos, video and scanning passport during identification. Your application is responsible to describe the reason why camera, microphone, photo library and NFC reader is used. You must add 4 descriptions listed below to info.plist of your application with the explanation of the usage.

Add required steps for NFC scanning

The application needs to define the list of application IDs or AIDs it can connect to, in the Info.plist file. The AID is a way of uniquely identifying an application on a ISO 7816 tag, which is usually defined by a standard.

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
  <string>A0000002471001</string>
  <string>A0000002472001</string>
  <string>00000000000000</string>
</array>

Add a new entitlement for NFC scanning, available since iOS 13. This new entitlement is added automatically by Xcode when enabling the Near Field Communication Tag Reading capability in the target Signing & Capabilities. After enabling the capability the *.entitlements file needs to contain the TAG format:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>NDEF</string>
    <string>TAG</string>
</array>

Starting verification flow

Import Veriff in your code

// Swift
import Veriff

// Objective-C
@import Veriff;

In order to use Veriff SDK, please import it to your class that will use the SDK.

Start the verification flow

In order to start the verification flow please call the method below with the defined parameters.

// Swift
let veriff = VeriffSdk.shared
veriff.startAuthentication(sessionUrl: sessionUrl, presentingFrom: yourViewController)

// Objective-C
VeriffSdk *veriff = [VeriffSdk shared];
[veriff startAuthenticationWithSessionUrl:sessionUrl presentingFrom:yourViewController];

Parameters are defined as below;

Parameters Description
sessionUrl Required parameter. sessionUrl can be received from your backend implementation. sessionUrl should be unique for each call. Check /sessions endpoint in the API documentation here to learn how to generate one.
configuration Optional VeriffConfiguration object. Refer to Customize user interface (Optional) for how to use it.
presentingFrom Your app's view controller from which our UI will be presented modally.

Customize user interface (Optional)

You can customize the Veriff user interface through your own application, by letting the SDK know of your brand's main color, font and logo. The Branding class allows customization of the theme color, navigation bar title image, button background color, height and corner radius, background color, status bar color, primary and secondary text colors, bullet point image, button title text casing and font. In order to use customization set the branding property of VeriffConfiguration before you start the verification.

See the Veriff SDK customization guide document to see what it looks like.

// Swift
let yourColor = UIColor.someColor()
let yourImage = UIImage(named: "logo.png")
let branding = VeriffSdk.Branding(themeColor: yourColor, logo: yourImage)
branding.primaryButtonBackgroundColor = UIColor.primaryButtonBackgroundColor
branding.buttonHeight = CGFloat.buttonHeight
branding.buttonCornerRadius = CGFloat.cornerRadius
branding.backgroundColor = UIColor.backgroundColor
branding.statusBarColor = UIColor.statusBarColor
branding.primaryTextColor = UIColor.primaryTextColor
branding.secondaryTextColor = UIColor.secondaryTextColor
branding.bulletPoint = UIImage(named: "bulletPoint.png")
branding.isTextUppercase = false
branding.font = VeriffSdk.Branding.Font(regularFontName: "Font", lightFontName: "Font-Light", semiBoldFontName: "Font-SemiBold", boldFontName: "Font-Bold")

// Objective-C
UIImage *logo = [UIImage imageNamed:@"logo.png"];
VeriffBranding *branding = [[VeriffBranding alloc] initWithThemeColor:[UIColor greenColor] logo:logo];
branding.primaryButtonBackgroundColor = [UIColor greenColor];
branding.buttonHeight = 56;
branding.buttonCornerRadius = 5;
branding.backgroundColor = [UIColor lightGrayColor];
branding.statusBarColor = [UIColor redColor];
branding.primaryTextColor = [UIColor darkTextColor];
branding.secondaryTextColor = [UIColor lightTextColor];
branding.bulletPoint = [UIImage imageNamed:@"bulletPoint.png"];
branding.isTextUppercase = false;
branding.font = [[VeriffBrandingFont alloc] initWithRegularFontName: @"Font" lightFontName: @"Font-Light" semiBoldFontName: @"Font-SemiBold" boldFontName: @"Font-Bold"];

Setting the user interface language

The Veriff iOS SDK allows setting the language of the SDK. In order to use this language, please set the languageLocale property of VeriffSdk.Configuration before you start the verification.

// Swift
let locale = Locale(identifier: "et")

// Objective-C
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"et"];

Create the configuration object

// Swift
let configuration = VeriffSdk.Configuration(branding: branding, languageLocale: locale)

// Objective-C
VeriffConfiguration *configuration = [[VeriffConfiguration alloc] initWithBranding:branding languageLocale:locale];

Custom intro screen

Veriff supports replacing introduction screen with a custom client developed introduction screen for eligible clients. First, please ask about this possibility from your account manager. In case we can offer it for you then removal process is following:

configuration.customIntroScreen = true

Note: This step alone is not enough to enable the custom intro screen. Make sure to contact your account manager so they can enable the feature for your integration.

Start the verification flow by using the configuration

// Swift
let veriff = VeriffSdk.shared
veriff.startAuthentication(sessionUrl: sessionUrl, configuration: configuration, presentingFrom: yourViewController)

// Objective-C
VeriffSdk *veriff = [VeriffSdk shared];
[veriff startAuthenticationWithSessionUrl:sessionUrl configuration:configuration presentingFrom:yourViewController];

Handling result codes from SDK

To receive session results, you must implement the VeriffSdkDelegate protocol and assign a delegate to the VeriffSdk instance.

veriff.delegate = self

The Veriff SDK returns a number of result codes that your application can handle.

// Swift
extension VerificationService: VeriffSdkDelegate {
    func sessionDidEndWithResult(_ result: Veriff.Result) {
        switch result.status {
        case .done:
            // The user successfully submitted the session
            break
        case .canceled:
            // The user canceled the verification process.
            break
        case .error(let error):
            switch error {
                // ...
            }
        }
    }
}

// Objective-C
- (void)sessionDidEndWithResult:(VeriffResult *)result {
    switch (result.status) {
    case VeriffStatusDone:
        // The user successfully submitted the session
        break;
    case VeriffStatusCanceled:
        // The user canceled the verification process.
        break;
    case VeriffStatusError:
        if (result.error == nil) { break; }
        switch (result.error.code) {
            // ...
        }
    }
}

You can find the description of status codes below:

Notes

Migrating to Veriff iOS 3.0.0

Switch from baseUrl and sessionToken to sessionUrl

The Veriff object in the SDK 3.0.0 takes a required sessionUrl and an optional VeriffConfiguration instance as parameters into initialisation. The sessionUrl is received from your backend implementation (see the documentation here), it is composed of the baseUrl and sessionToken sent to the VeriffConfiguration object in earlier versions.

Updated VeriffConfiguration object

The VeriffConfiguration struct now takes in branding and languageLocale as initialisation parameters.

The new VeriffDelegate method

You can now receive session results via func sessionDidEndWithResult(_ result: Veriff.Result) instead of the obsolete func onSession(result: VeriffResult, sessionToken: String). The sessionToken is included in the Veriff.Result as an instance variable.

Use Veriff.Result instead of VeriffResult

The new Veriff.Result struct comprises status: Status and sessionToken: String? instance variables. The Status enum can be of three types: done, canceled, error. The description variable on the Veriff.Result returns additional information as a string. See the example here.

Migrating to Veriff iOS 4.0.0

Rename Veriff instances to VeriffSdk

The name Veriff was used both for our module and public class name. However this blocked us from supporting Swift Package Manager due to Swift compiler bug. We renamed our public class name to VeriffSdk.

VeriffConfiguration is now VeriffSdk.Configuration

VeriffConfiguration struct is moved under VeriffSdk. Please replace the occurrences of VeriffConfiguration with VeriffSdk.Configuration.

Branding is now VeriffSdk.Branding

Branding struct is moved under VeriffSdk. Please replace the occurrences of Branding with VeriffSdk.Branding.

Rename VeriffDelegate to VeriffSdkDelegate

Please replace the occurrences of VeriffDelegate with VeriffSdkDelegate.

VeriffSdk.Result.description made non-optional

If you were dealing with unwrapping it, feel free to remove it.

VeriffSdk.Result.sessionToken removed and VeriffSdk.Result.sessionUrl added instead

Please remove the occurrences of sessionToken. You can now use sessionUrl to get the full sessionUrl including token.

iOS SDK Changelog

Please refer to release notes.

Flutter Plugin integration

Requirements

Integration with Veriff Flutter Plugin requires the project to target at least iOS version 9.0 and Android version 5.0 (api 21) or higher.

Install the plugin to your project

Follow instructions here to install the plugin.

Android specific configuration

If you want to customize the notification icon please add the icon to your flutter app`s Android resources at android/res/drawable

iOS specific configuration

Add usage descriptions to application Info.plist

Not adding these usage descriptions causes system to kill application when it requests the permissions when needed.

Veriff requires camera, microphone and NFC reader permissions for capturing photos, video and scanning passport during identification. Your application is responsible to describe the reason why camera, microphone and NFC reader is used. You must add 3 descriptions listed below to Info.plist of your application with the explanation of the usage.

Add required steps for NFC scanning

The application needs to define the list of application IDs or AIDs it can connect to, in the Info.plist file. The AID is a way of uniquely identifying an application on a ISO 7816 tag, which is usually defined by a standard.

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
  <string>A0000002471001</string>
  <string>A0000002472001</string>
  <string>00000000000000</string>
</array>

Add a new entitlement for NFC scanning, available since iOS 13. This new entitlement is added automatically by Xcode when enabling the Near Field Communication Tag Reading capability in the target Signing & Capabilities. After enabling the capability the *.entitlements file needs to contain the TAG format:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>NDEF</string>
    <string>TAG</string>
</array>

Set the iOS target in Xcode

Make sure that the 'iOS Deployment Target' in Xcode (under Project > target > Info > Deployment Target) is set to iOS 9.0 or later.

Launching the verification flow

Import plugin in your code

In order to use Veriff plugin, please import it to your class that will use it. import 'package:veriff_flutter/veriff_flutter.dart'

Start verification flow

In order to start the verification flow please create a configuration with defined parameters below.

Parameters are defined as below;

Parameters Description
sessionUrl Required parameter. sessionUrl can be received from your backend implementation. sessionUrl should be unique for each call. Check /sessions endpoint in the API documentation here to learn how to generate one.
Configuration config = Configuration(sessionUrl);

Then pass the configuration to Veriff object and start the verification flow;

Veriff veriff = Veriff();

try {
      Result result = await veriff.start(config);
      print(result.status);
      print(result.error);
    } on PlatformException {
      // handle exception
    }

Customize user interface (Optional)

You can customize Veriff SDK user interface in your application by defining your brand main color and logo.

See the Veriff SDK customization guide document to see what it looks like.

Veriff Flutter plugin allows the customization of UI elements and icons in the SDK flow by passing the optional parameters when launching Veriff;

AssetImage logo = AssetImage(path_of_image);
Branding branding = Branding(
        themeColor: "#ff00ff",
        backgroundColor: "#f2ff00",
        statusBarColor: "#ff7700",
        primaryTextColor: "#52b35c",
        secondaryTextColor: "#3a593d",
        buttonCornerRadius: 5,
        logo: logo,
        androidNotificationIcon: "ic_notification",
        primaryButtonBackgroundColor: "#123abc"
      );

Note: If you are customizing androidNotificationIcon don't forget to add the icon to the android/res/drawable folder. Add notification icon

And pass the branding object with configuration for starting the verification flow;

Configuration config = Configuration(sessionUrl, branding: branding);

When a color isn't defined, the default Veriff theme color is used. Same applies to image assets - when they aren't defined, the defaults are used.

Setting the user interface language

Veriff Flutter plugin supports setting the language of the UI. In order to use this feature, please pass the locale identifier as in example below;

Configuration config = Configuration(sessionUrl, branding: branding, languageLocale: "et");

Custom intro screen

Veriff supports replacing introduction screen with a custom client developed introduction screen for eligible clients. First, please ask about this possibility from your account manager. In case we can offer it for you then removal process is following:

config.useCustomIntroScreen = true;

Handling the results from plugin

The Result returned by start method will have a status that is one of Status.done, Status.canceled and Status.error. In case Status.error is received, you will also have an error description that is one of the list below;

You can check the statuses and errors using switch-case as in example below;

switch (result.status) {
        case Status.done:
          print("Session is completed.");
          break;
        case Status.canceled:
          print("Session is canceled by the user.");
          break;
        case Status.error:
          switch (result.error) {
            case Error.cameraUnavailable:
              print("User did not give permission for the camera");
              break;
            case Error.microphoneUnavailable:
              print("User did not give permission for the microphone.");
              break;
            case Error.networkError:
              print("Network error occurred.");
              break;
            case Error.sessionError:
              print("A local error happened before submitting the session.");
              break;
            case Error.deprecatedSDKVersion:
              print(
                  "Version of Veriff SDK used in plugin has been deprecated. Please update to the latest version.");
              break;
            case Error.unknown:
              print("Uknown error occurred.");
              break;
            case Error.nfcError:
              print("Error with NFC");
              break;
            case Error.setupError:
              print("Error with setup");
              break;
            case Error.none:
              print("No error.");
              break;
            default:
              break;
          }
          break;
        default:
          break;
}

React Native SDK integration

React Native SDK requirements

Integration with Veriff React Native SDK requires the project to target at least iOS version 11.0 and Android version 5.0 (api 21) or higher.

Note: If you have created your react native app using expo, you will have to eject out of expo at this point(if you have not ejected out already) since expo does not support native modules Check the Caveats section here for more info on that.

Add the SDK to a project

Add the Veriff React Native SDK to your package.json file:

npx yarn add @veriff/react-native-sdk

Or if using npm:

npm install @veriff/react-native-sdk --save

Update Android build.gradle file

Open the root build.gradle file in the android folder and add a new maven destination to the repositories in the allprojects section. The best place is right after the local node_modules repositories and before the google() repository.

allprojects {
    repositories {
        // ... local react native repos
        maven { url "https://cdn.veriff.me/android/" } //veriff
        google()
        jcenter()
    }
}

iOS Specific setup

Add Swift to the ios module

Veriff SDK requires your ios module to have some Swift code. If you don't have any Swift code yet then add a new empty.swift file in Xcode. If Xcode offers to add a bridging header then add it.

Update iOS Podfile

Open Podfile in the ios folder and make sure that platform is 11.0 or higher:

platform :ios, '11.0'

Also make sure your main target in the Podfile contains use_native_modules! directive:

target 'MyApp' do
  # pod 'foo'...

  config = use_native_modules!
end

Add usage descriptions to application Info.plist

Not adding these usage descriptions causes system to kill application when it requests the permissions when needed.

Veriff requires camera, microphone and NFC reader permissions for capturing photos, video and scanning passport during identification. Your application is responsible to describe the reason why camera, microphone and NFC reader is used. You must add 3 descriptions listed below to Info.plist of your application with the explanation of the usage.

Add required steps for NFC scanning

The application needs to define the list of application IDs or AIDs it can connect to, in the Info.plist file. The AID is a way of uniquely identifying an application on a ISO 7816 tag, which is usually defined by a standard.

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
  <string>A0000002471001</string>
  <string>A0000002472001</string>
  <string>00000000000000</string>
</array>

Add a new entitlement for NFC scanning, available since iOS 13. This new entitlement is added automatically by Xcode when enabling the Near Field Communication Tag Reading capability in the target Signing & Capabilities. After enabling the capability the *.entitlements file needs to contain the TAG format:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>NDEF</string>
    <string>TAG</string>
</array>

Set the iOS target in Xcode

Make sure that the 'iOS Deployment Target' in Xcode (under Project > target > Info > Deployment Target) is set to iOS 9.0 or later.

Using the Veriff React Native SDK

Starting the verification flow

In order to use Veriff SDK, please import it to the file that will use the SDK.

import VeriffSdk from '@veriff/react-native-sdk';

Parameters are defined as below;

Parameters Description
sessionUrl Required parameter. sessionUrl can be received from your backend implementation. sessionUrl should be unique for each call. Check /sessions endpoint in the API documentation here to learn how to generate one.

Once you have the sessionUrl you can launch Veriff using VeriffSdk imported earlier:

var result = await VeriffSdk.launchVeriff({ sessionUrl: SESSION_URL });

User Interface customization

You can customize Veriff SDK user interface in your application by defining your brand main color and logo.

See Veriff SDK customization guide document on how it looks.

Veriff React Native SDK allows the customization of various colors, images and fonts in the SDK flow by passing in these optional properties when launching Veriff:

var result = await VeriffSdk.launchVeriff({
    sessionUrl: SESSION_URL,
    branding: {
      themeColor: '#ff00ff',
      backgroundColor: '#ffffff',
      statusBarColor: '#ff00ff',
      primaryTextColor: '#000000',
      secondaryTextColor: '#333333',
      primaryButtonBackgroundColor: '#444444',
      buttonCornerRadius: 28,
      logo: 'parrot', // see alternative options for logo below
      androidNotificationIcon: 'ic_parrot',
      iOSFont: {
        regularFontName: 'Font-Regular',
        lightFontName: 'Font-Light',
        semiBoldFontName: 'Font-Semibold',
        boldFontName: 'Font-Bold'
      },
      androidFont: {
        regularFontName: 'font_regular',
        boldFontName: 'font_bold',
      }
    },
  });

When a color or a font isn't defined the default Veriff theme color or font is used. Same applies to image assets - when they aren't defined the defaults are used. The image assets need to be added into Xcode assets in the iOS project and into drawable folders in the Android project. In the example above you would need to add an image asset named 'parrot' into Xcode assets and both 'parrot.png' and 'ic_parrot.png' to Android drawable folders in android/src/main/res.

Instead of using platform-specific image assets you can provide a URI to an image which will then be used:

var result = await VeriffSdk.launchVeriff({
    sessionUrl: SESSION_URL,
    branding: {
      themeColor: '#ff00ff',
      logo: { uri: 'http://example.com/logo/parrot.jpg' },
    },
  });

React Native assets are also supported through resolveAssetSource:

const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource');
const parrot = require('./img/parrot.png');

var result = await VeriffSdk.launchVeriff({
    sessionUrl: SESSION_URL,
    branding: {
      themeColor: '#ff00ff',
      logo: resolveAssetSource(parrot),
    },
  });

Setting the user interface language

The Veriff RN SDK allows setting the language of the SDK. In order to use this language, please pass the locale as in example below;

var result = await VeriffSdk.launchVeriff({
    sessionUrl: SESSION_URL,
    branding: {
      themeColor: '...',
    },
    locale: 'et'
  });

Custom intro screen

Veriff supports replacing introduction screen with a custom client developed introduction screen for eligible clients. Please request this from your account manager. The removal process is following:

var result = await VeriffSdk.launchVeriff({
    sessionUrl: SESSION_URL,
    branding: {
      themeColor: '...',
    },
    locale: 'et',
    customIntroScreen: true
  });

Handling the result from the SDK

The result returned by launchVeriff will have a status field that is one of either VeriffSdk.statusDone, VeriffSdk.statusCanceled or VeriffSdk.statusError. In case of statusError there will be a error code (not human-readable) in the error field.

var result = await VeriffSdk.launchVeriff({ sessionUrl: SESSION_URL });
switch (result.status) {
  case VeriffSdk.statusDone:
    // user submitted the images and completed the flow
    // note that this does not mean a final decision yet
    break;
  case VeriffSdk.statusCanceled:
    // user canceled the flow before completing
    break;
  case VeriffSdk.statusError:
    // the flow could not be completed due to an error
    console.log("Veriff verification failed with error=" + result.error);
    break;
}

Migrating to React Native SDK 2.0.0

Follow these steps to migrate to React Native SDK 2.0.0 API

Use new parameter sessionUrl

New API uses sessionUrl instead of a sessionToken. sessionUrl is a combination of base URL and session token.

We renamed the navigationBarImage parameter in configuration to logo.

React Native SDK Changelog

Below is a summary of changes introduced with each version of the React Native SDK

Versions
    Release 2.5.0
    • Added new customization option to provide background color for primary button.
    Release 2.4.0
    • Added implmentation type parameter.
    Release 2.3.2
    • Updated public readme of the package in npmjs, no functional changes.
    Release 2.3.1
    • Updated public readme of the package in npmjs, no functional changes.
    Release 2.3.0
    • Added new customization options to provide background, status bar and text colors.
    • Added an option to customize primary button corner radius.
    Release 2.2.0
    • The logo customization option now supports URLs and RN assets.
    Release 2.1.0
    • Updated the iOS native module to use Veriff iOS SDK 4.+
    Release 2.0.0
    • New public API for the React Native SDK.
    • Updated the iOS native module to use Veriff iOS SDK 3.+
    Release 1.1.0
    • Updated the Android native module to use Veriff Android SDK 3.+
    • Enabled Java 8 support for Android.
    • Added more granular error messages for NFC related errors.
    Release 1.0.1
    • Fixed a 'NoSuchKeyException: branding' error on Android if no customization was applied
    Release 1.0.0
    • Added support for UI customization
    Release 0.9.1
    • Updated README in npmjs.com
    Release 0.9.0
    • Initial React Native SDK release

Javascript SDK Integration

Veriff JS SDK, is a simple and customisable library which helps to integrate with Veriff Online Identity solution. Use this to simply include Veriff flow. You don't need any backend on your side for this including JS SDK to your website.

Install JS SDK

Include as a script tag:

<script src='https://cdn.veriff.me/sdk/js/1.1/veriff.min.js'></script>

or install it via a package manager

  $ npm install --save @veriff/js-sdk
   // CommonJS
  const Veriff = require('@veriff/js-sdk');

  // ES6 style import
  import { Veriff } from '@veriff/js-sdk';

Adding JS SDK

Veriff JS SDK requires one parent element in HTML:

<div id='veriff-root'></div>

It is possible to set the width of js-sdk form through style attribute:

<div id='veriff-root' style="width:400px"></div>

In order to initialize the library, API Key, parentId and onSession callback function is required. Parameters are defined as below;

Parameters Description
apiKey Required parameter. See how to find your API keys here to learn how to generate one.
parentId Required parameter. 'veriff-root'
onSession Required parameter. The user can be redirected either to the verification url or incontext SDK can be triggered
  const veriff = Veriff({
    apiKey: 'API_KEY',
    parentId: 'veriff-root',
    onSession: function(err, response) {
      // received the response, verification can be started / triggered now

      // redirect
      window.location.href = response.url;

      // incontext sdk
      createVeriffFrame({url: response.url});
    }
  });

  veriff.mount();

Read more about incontext sdk method or redirection.

By default the following form will be rendered:

JSSDKDEFAULT

onSession function is executed after the response is received from the API, response body contains a verification object with following schema:

    {
      "status": "success",
      "verification": {
          "id": "UUID V4 Identifying the verification",
          "url": "full url to which a person should be redirected in order to proceed with verification flow",
          "host": "hostname",
          "status": "status of the verification",
          "sessionToken": "JWT encoded verification token"
      }
  }

vendorData: string - Client specific data string, max 400 characters long, will be sent back unmodified using webhooks. We require only non-semantic data to be submitted (UUID-s etc that can not be resolved or used outside of the vendor environment). In case the Given name / Last name / Vendor Data or all of them are known, they can be passed to the SDK, therefore text input fields will not be rendered.

  const veriff = Veriff({
    apiKey: 'API_KEY',
    parentId: 'veriff-root',
    onSession: function(err, response) {
      // received the response, verification can be started now
    }
  });
  veriff.setParams({
    person: {
      givenName: 'Foo',
      lastName: 'Bar'
    },
    vendorData: '7eb19312-79d6-11ec-90d6-0242ac120003'
  });
  veriff.mount({
    submitBtnText: 'Get verified'
  });

JSSDKONLYBUTTON

It is possible to disable fields rendering without passing any data by not including anything in corresponding value:

  const veriff = Veriff({
    apiKey: 'API_KEY',
    parentId: 'veriff-root',
    onSession: function(err, response) {
      // received the response, verification can be started now
    }
  });
  veriff.setParams({
    person: {
      givenName: ' ',
      lastName: ' '
    },
    vendorData: ' '
  });
  veriff.mount({
    submitBtnText: 'Get verified'
  });

Additionally the input placeholder and button text value can be customised.

  const veriff = Veriff({
    apiKey: 'API_KEY',
    parentId: 'veriff-root',
    onSession: function(err, response) {
      // received the response, verification can be started now
    }
  });
  veriff.mount({
    formLabel: {
      givenName: 'First name',
      lastName: 'Family name',
      vendorData: 'Unique id of a user'
    },
    submitBtnText: 'Get verified',
    loadingText: 'Please wait...'
  });

JSSDKCUSTOM

InContext Javascript SDK Integration

Use this if you would like to use incontext UI.

Install InContext JS SDK

npm install @veriff/incontext-sdk

Adding InContext JS SDK

You need to have a Veriff session URL generated before initializing the SDK. Please, see our documentation to learn how to generate one.

// ES6
import { createVeriffFrame } from '@veriff/incontext-sdk';

// CommonJS
const { createVeriffFrame } = require('@veriff/incontext-sdk');

const veriffFrame = createVeriffFrame({ url: VERIFF_SESSION_URL })

This will render a modal with adapted Veriff application in iframe.

createVeriffFrame() returns an object that controls the modal.

Listening for events

Pass onEvent callback function when initializing SDK

import { createVeriffFrame, MESSAGES } from '@veriff/incontext-sdk';

createVeriffFrame({
  url,
  onEvent: function(msg) {
    switch(msg) {
      case MESSAGES.CANCELED:
        //
        break;
    }
  }
})

Using with inline script

Include a script tag:

<script src='https://cdn.veriff.me/incontext/js/v1/veriff.js'></script>
window.veriffSDK.createVeriffFrame({ url: VERIFF_SESSION_URL });

Options

createVeriffFrame([options])

Close Veriff modal

Normally modal will be closed due to user input, but if you want to control it(e.g. timeout), please use this function.

veriffFrame.close();

onReload

This callback can be called on some browsers when a user has denied camera access and to re-request access the host page has to be reloaded.

For best UX we recommend reloading the webpage & reopening Veriff InContext SDK automatically.

Example:

const url = sessionStorage.getItem('@veriff-session-url') || getSessionUrl()

veriffSDK.createVeriffFrame({
  url,
  onReload: () => {
    // Logic to re-open Veriff SDK after page reload
    // e.g. sessionStorage.setItem('@veriff-session-url', url);
    window.location.reload();
  },
});

Adding Content Security Policy to InContext SDK

add_header Content-Security-Policy default-src 'self' *.veriff.me *.veriff.com;
script-src 'unsafe-inline' 'self' *.veriff.me *.veriff.com *.google-analytics.com *.googletagmanager.com *.hotjar.com *.probity.io;
img-src blob: 'self' *.probity.io *.google-analytics.com;
frame-src 'self' *.hotjar.com;
connect-src 'self' *.veriff.com *.veriff.me *.probity.io;
style-src 'unsafe-inline' 'self' *.veriff.com *.veriff.me;

Integrate by redirection

In case you would like to opt out of modal based approach by using our incontext sdk you can instead redirect users to the end user flow.

Using redirection

You need to have a Veriff session URL generated before redirecting the user to the flow. Please, see our documentation to learn how to generate one.

// After receiving the url you could redirect users there by using following
window.location.href = url;

Query parameters

Available parameters that can be changed;

Parameters Description
lang Optional parameter. lang can be used to override the user interface's language.

API Integration

There are a few scenarios where you may not wish to use Veriff's native SDK or Veriff's web interface to verify your customers. For example:

In those cases, you can do the whole process using our API, according to the documentation, and not show any Veriff front end to your customers, or not expect customers to be present for the verification.

Here are the steps you should do to use the API for upload.

Create a new verification session

Create a new verification session using POST request to #sessions

The goal here is to create a new object (a verification session) that contains the one verification (referred to as 'verification', nested inside the session object in the response).

If you wish to restrict the accepted document to be the one you have on file, you can also send the document type, country, and number along with the session. Document type can be one of [‘PASSPORT’, ‘ID_CARD’, ‘RESIDENCE_PERMIT’, ‘DRIVERS_LICENSE'].

Send photos

Send photos (face, document front, document back, etc) by uploading all the pictures one by one using POST request to ‘/media’

The goal here is to upload the required photos and associate them with the verification created in step 1.

Documentation: /sessions/{sessionId} /media (POST)

Submit session for review

Submit the verification session using PATCH request to ‘/sessions/:id’

Once all the photos are uploaded, you would then update (PATCH) the verification to mark it into 'submitted' status. This marks the completion of the verification. This step requires all the photos to be submitted prior to triggering this.

Documentation: /sessions/{sessionid} (PATCH)

After these three steps, you've done your part, and the verification will then be taken care of by us.

Wait for webhook response

Veriff sends you a Decision event via Webhook using POST request. You will have to implement the listener.

This hook will be sent back asynchronously, and it contains more data, including the verified identity information.

Documentation: Webhook Decision (POST)

The webhook is sent to the URL that is configurable from Veriff Station under Integrations › Webhook Decision / Events URL.

SDK Support Policy

Support Policy for Veriff SDKs

Identity verification software, practices, and regulations move fast. To keep our software user-friendly, and maintain high conversion rates for our clients, our development team needs to be faster. To deliver an increasingly smarter, better and safer product, it's important to drop backward compatibility layers and update SDKs at least once every three months. Our SDKs are updated every two weeks, and downloading new releases will keep your verification process smooth and error-free. All releases are fully tested by our team and before being deployed, and come with the support from our team in the form of:

SDK release versioning To help you keep track of SDK releases, Veriff uses three numbers to represent the scale of the updates. Each release is assigned either X, Y, or Z according to the following descriptions:

Supported languages

Supported languages are as below;

Language Code Name
ar العربية
bg Български
bn বাংলা
ca Català
cs Čeština
da Dansk
de Deutsch
el Ελληνικά
et Eesti
en English
es Español (España)
es-latam Español (Latinoamérica)
es-mx Español (México)
fi Suomi
fil Filipino
fr Français
hi हिंदी
hr Hrvatski
hu Magyar
id Bahasa Indonesia
it Italiano
ja 日本語
ka ქართული
ko 한국어
lv Latviešu
lt Lietuvių
mk Македонски
ms Bahasa Melayu
nb Norsk (Bokmål)
nl Nederlands
pl Polski
pt Português (Brasil)
pt-pt Português (Portugal)
ro Română
ru Русский
si සිංහල
sk Slovenčina
sl Slovenski
so Af-soomaali
sr Srpski
sv Svenska
tr Türkçe
th ไทย
uk Українська
vi Tiếng Việt
zh 简体中文
zh-hant 繁體中文
hy հայերեն

Testing Integrations

You will have 1 Test integration pre-created for you in Veriff Station. You can create more test integrations if needed. The test integration will be used by you to test the communication between Veriff's and your system and to see how the responses are handled. Verification sessions generated for test integrations are not billed and not verified by Veriff.

To check quality of Veriff responses, please select plan and subscribe to a Trial on Billing page - this will allow to create Live integrations.

Testing verification sessions checklist

Also see our support article for getting the desired test verification result

Testing security

Testing responses

Each type of response should be accepted:

Testing process in your app

You should test the handling of best cases as well as edge cases

Mobile testing

Test our demo app by downloading it in the app store. iOS / Android

API Upload testing

Required tools Veriff's integration demo - js-integration-demo-master.zip Node.js - (Download - https://nodejs.org/en/download/) Notepad/TextEdit (default in Windows/Mac) or Notepad++ - (Download - https://notepad-plus-plus.org/)

  1. Download and install Node.js
  2. Download and extract js-integration-demo-master.zip
  3. Open Command (Windows) or Terminal (Mac) on your local computer.
  4. Navigate to js-integration-demo-master folder cd C:\Users\User\Desktop\Veriff API).
  5. Run command >npm install
  6. Open app.js with your text editing app (Notepad/TextEdit) and update 'API-Public-Key' and 'API-Private-Key' to the values in your Backoffice account (Management -> Vendor). Tokens must be in single quotes. Save the file.
const API_TOKEN = process.env.API_TOKEN || 'API-Public-Key';
const API_SECRET = process.env.API_SECRET || 'API-Private-Key';
const API_URL = process.env.API_URL || 'https://api.staging.vrff.io/v1';
const WEBHOOK_PORT = process.env.WEBHOOK_PORT || 3002;
const IMAGE_DIR = process.env.IMAGE_DIR || 'documents'

Run the app.js node app.js

Now the verification session is created and it is being processed. Check your Backoffice dashboard to review the data extracted and decision made by Veriff.

Batch testing

Batch upload tests are sometimes agreed with our onboarding team and are very use case specific. Therefore below might be not applicable for most of the clients. For batch tests customers will need to prepare dataset that will be used for testing. If you have been asked for dataset please see batch test data preparetion guide.

End-User In The Flow

Flow Diagram

Session Status Diagram

Session Status

API Reference

Backwards compatible changes

Following changes are considered to be backwards compatible by Veriff:

Requirements

API public key and API private key can be found in customers integration settings page.

Headers

X-AUTH-CLIENT: string (required) - API public key
CONTENT-TYPE: string (required) - Media type of the resource (application/json)
X-HMAC-SIGNATURE: string (required) - HMAC-SHA256 hex encoded keyed hash using your API private key

API URL https://<Base-URL>/v1/

Note: to get Base URL, choose 'Integrations' in the top menu of Veriff Station, then integration you need.

Lifecycle of a verification:

Sessions Lifecycle

Endpoints

In this post request the vendor sends the data of the client to Veriff and in return, recieves a unique sessionToken that is related to the client. You can find the sample implementation for Javascript. https://github.com/Veriff/js-integration-demo

The URIs have the following structure:

https://<Base-URL>/v1/

For example, https://your-veriff-baseUrl/v1/sessions/

Note: to get Base URL choose Integrations in the top menu of Veriff Station, then integration you need.

Available endpoints and methods:

POST /sessions

Creates a session with specified verification data.

Request method: POST
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
Content-Type: application/json

Request properties explained

Sample Request

curl

curl -X POST \
  --url '/v1/sessions/' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -d '{
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith",
            "idNumber": "123456789"
        },
        "document": {
            "number": "B01234567",
            "type": "PASSPORT",
            "country": "EE"
        },
        "vendorData": "11111111",
        "timestamp": "2016-05-19T08:30:25.597Z"
    }
}'

Node.js

var request = require('request');

var options = { method: 'POST',
  url: '/v1/sessions/',
  headers:
   { 'Content-Type': 'application/json',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' },
  body:
   { verification:
      { callback: 'https://veriff.com',
        person:
         { firstName: 'John',
           lastName: 'Smith',
           idNumber: '123456789' },
        document: { number: 'B01234567', type: 'PASSPORT', country: 'EE' },
        vendorData: '11111111',
        timestamp: '2016-05-19T08:30:25.597Z' } },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3

import requests
import pprint
import json

url = '/v1/sessions/'

payload = json.dumps({
    'verification': {
        'callback': 'https://veriff.com',
        'person': {
          'firstName': 'John',
          'lastName': 'Smith',
          'idNumber': '123456789'
        },
        'document': {
          'number': 'B01234567',
          'type': 'PASSPORT',
          'country': 'EE'
        },
        'vendorData': '11111111',
        'features': [
          'selfid'
        ],
        'timestamp': '2016-05-19T08:30:25.597Z'
    }
})

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'Content-Type': 'application/json'
}

response = requests.request('POST', url, data=payload, headers=headers)
pprint.pprint(response.json())

Response

Response properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample RESPONSE

{ "status": "success",
  "verification":{
    "id":"f04bdb47-d3be-4b28-b028-a652feb060b5",
    "url": "https://alchemy.veriff.com/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214",
    "vendorData": "11111111",
    "host": "https://alchemy.veriff.com",
    "status": "created",
    "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214"
  }
}

POST /sessions/{sessionId}/media

In this post request the vendor sends a file (base64 encoded image string inside a JSON body object), where they could upload images (1 image at a time), specifying also a type of the image that is being uploaded.

Request

Request properties explained:

Request method: POST
Media type: application/json
Type: object
Headers:
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample REQUEST

curl

curl -X POST \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53' \
  -d '{
    "image": {
    "context": "document-front",
      "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII=",
      "timestamp": "2019-10-29T06:30:25.597Z"
    }
}'

Node.js

var request = require('request');

var options = { method: 'POST',
  url: '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' },
  body:
   { image:
      { context: 'document-front',
        content: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII=',
        timestamp: '2019-10-29T06:30:25.597Z' } },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url='/v1/sessions/{sessionId}/media'

payload = json.dumps({
    'image': {
    'context': 'document-front',
      'content': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+.../9fgAEAKcxisFjVfn0AAAAASUVORK5CYII=',
      'timestamp': '2019-10-29T06:30:25.597Z'
    }
})


headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53',
    'Content-Type': 'application/json'
}

response = requests.request('POST', url, data=payload, headers=headers)
pprint.pprint(response.json())

Response:

Response properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample RESPONSE

{ "status": "success",
  "image":{
     "context": "document-back",
     "id": "39388f8d-c6d6-4e9b-92c6-6978b2e8d664",
     "name": "document-back",
     "timestamp": null,
     "size": 52268,
     "mimetype": "image/png",
     "url": "/v1/media/39388f8d-c6d6-4e9b-92c6-6978b2e8d664"
   }
}

PATCH /sessions/{sessionId}

Method to change the status of the verification.

Request properties explained:

Request method: PATCH
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample REQUEST

curl

curl -X PATCH \
  --url '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0' \
  -d '{
    "verification": {
      "status": "submitted",
      "timestamp": "2019-10-29T06:30:25.597Z"
    }
}'

Node.js

var request = require('request');

var options = { method: 'PATCH',
  url: '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' },
  body:
   { verification:
      { status: 'submitted',
        timestamp: '2019-10-29T06:30:25.597Z' } },
  json: true };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/{sessionId}'

payload = json.dumps({
    'verification': {
      'status': 'submitted',
      'timestamp': '2019-10-29T06:30:25.597Z'
    }
})

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0',
    'Content-Type': 'application/json'
}

response = requests.request('PATCH', url, data=payload, headers=headers)
pprint.pprint(response.json())

Response

Response properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample RESPONSE

{
    "status": "success",
    "verification": {
        "id": "fd5c1563-1d23-4b1a-ae46-7ba429927ed8",
        "url": "https://alchemy.veriff.com/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiZmQ1YzE1NjMtMWQyMy00YjFhLWFlNDYtN2JhNDI5OTI3ZWQ4IiwiaWF0IjoxNTcyMzM4MDIwfQ.3HbNq0YWKAfFrH-P658_WXMwcUMubyC1aXAMo-umfCU",
        "vendorData": "11111111",
        "host": "https://alchemy.veriff.com",
        "status": "submitted",
        "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiZmQ1YzE1NjMtMWQyMy00YjFhLWFlNDYtN2JhNDI5OTI3ZWQ4IiwiaWF0IjoxNTcyMzM4MDIwfQ.3HbNq0YWKAfFrH-P658_WXMwcUMubyC1aXAMo-umfCU"
    }
}

GET /sessions/{sessionId}/attempts

Get the list of attempt objects with sessionId = {sessionId}

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/attempts' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/attempts',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/attempts'

headers = {Response
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Response properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample Response

{
  "status": "success",
  "verifications": [
      {
        "id": "f5c68aea-7f4d-478d-80ab-ca9356074f69",
        "status": "approved"
      },
      {
        "id": "f2270684-8c51-4d03-88eb-dafd43e8b486",
        "status": "resubmission_requested"
      }
  ]
}

GET /sessions/{sessionId}/decision

Get the session decision with sessionId = {sessionId}

Note: to have verification object filled, it is necessary to have a webhook url configured for the integration which sessionId is linked with.

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/decision' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: 'https://<Base-URL>/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/decision',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = 'https://<Base-URL>/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a/decision'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Get the session decision with sessionId = {sessionId}

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

GET /sessions/{sessionId}/person

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private key
Content-Type: application/json

Sample request

curl

curl -X GET \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/person' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/person',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/person'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample response from primary data provider

{
    "status": "success",
    "person": {
        "id": "uuid",
        "firstName": "string",
        "lastName": "string",
        "idCode": "string",
        "dateOfBirth": "string, format (YYYY-MM-DD)",
        "gender": "string, values can be M - male, F - female, null - undefined",
        "nationality": "string, iso2 country code",
        "placeOfBirth": "string, place of birth",
        "citizenships": (Deprecated) [],
        "pepSanctionMatches": [
            {
                "numberOfMatches": "number",
                "providerSearchId": null,
                "date": "date with timestamp, 2018-11-28T17:13:28.154Z",
                "matches": [
                    {
                        "name": "string",
                        "nationality": "string",
                        "category": "string, SIP or PEP"
                    },
                ],
                "hits": []
            }
        ]
    }
}

Sample response from alternative data provider

{
    "status": "success",
    "person": {
        "id": "uuid",
        "firstName": "string",
        "lastName": "string",
        "idCode": "string",
        "dateOfBirth": "string, format (YYYY-MM-DD)",
        "gender": "string, values can be M - male, F - female, null - undefined",
        "nationality": "string, iso2 country code",
        "placeOfBirth": "string, place of birth",
        "citizenships": (Deprecated) [],
        "pepSanctionMatches": [
            {
                "providerSearchId": "string"
                "numberOfMatches": "number",
                "date": "date with timestamp, 2018-11-28T17:13:28.154Z",
                "matches": [],
                "hits": [
                    {
                      "doc": {
                        "aka": [
                          { "name": "string" },
                        ],
                        "associates": [
                          {
                            "association": "string",
                            "name": "string"
                          },
                        ],
                        "entity_type": "string, values can be person or organisation",
                        "fields": [
                          {
                            "name": "string",
                            "source": "string",
                            "value": "string",
                            "tag": "string",
                            "locale": "string"
                          },
                        ],
                        "id": "string",
                        "keywords": ["string"],
                        "last_updated_utc": "date with timestamp, 2018-11-28T17:13:28Z",
                        "name": "string",
                        "source_notes": {},
                        "sources": ["string"],
                        "types": ["string"]
                      },
                      "match_types": ["string, values can be: name_exact, aka_exact, name_fuzzy, aka_fuzzy, phonetic_name, phonetic_aka, equivalent_name, equivalent_aka, unknown"],
                      "match_types_details": {
                        "string": {
                          "match_types": {
                            "string": ["string, values can be: name_exact, aka_exact, name_fuzzy, aka_fuzzy, phonetic_name, phonetic_aka, equivalent_name, equivalent_aka, unknown"],
                          },
                          "type": "string"
                        },
                      },
                      "score": "decimal",
                      "match_status": "string, can be: no_match, false_positive, potential_match, true_positive, unknown, true_positive_approve, true_positive_reject",
                      "is_whitelisted": "boolean"
                    },
                ]
            }
        ]
    }
}

GET /sessions/{sessionId}/media

Get the list of media objects with sessionId = {sessionId}

If fetched within 7 days of session submit, the list of objects will be sorted by most relevant-> least relevant.

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private Key
Content-Type: application/json

Sample REQUEST:

curl

curl -X GET \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14'

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/media'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Response properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample RESPONSE

{
  "status": "success",
  "videos": [
    {
      "context": "{VIDEO_TYPE}",
      "duration": "{DURATION_IN_SECONDS}",
      "id": "{MEDIA_ID}",
      "mimetype": "{MEDIA_MIME_TYPE}",
      "name": "{VIDEO_NAME}",
      "sessionId": "{SESSIOND_ID}",
      "size": "{SIZE_IN_B}",
      "timestamp": null(deprecated),
      "url": "{MEDIA_DOWNLOAD_URL}"
    }
  ],
  "images": [
    {
      "context": "{IMAGE_TYPE}",
      "id": "{MEDIA_ID}",
      "name": "{IMAGE_NAME}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "sessionId": "{SESSIOND_ID}",
      "timestamp": null(deprecated),
      "size": "{SIZE_IN_B}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ]
}

GET /sessions/{sessionId}/watchlist-screening

Request method: GET
Media type: application/json
Type: object.
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private key
Content-Type: application/json

Sample request

curl

curl -X GET \
  --url '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/watchlist-screening' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/watchlist-screening',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/watchlist-screening'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Response Properties:

Sample response

no match

{
    "status": "success",
     "data": {
         "attemptId": "aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7",
         "vendorData": null,
         "checkType": "initial_result",
         "matchStatus": "no_match",
         "searchTerm": {
             "name": "Juan Rico"
         },
         "totalHits": 0,
         "createdAt": "2021-06-15T08:27:33.015Z",
         "hits": []
     }
}

possible match

{
    "status": "success",
    "data": {
        "attemptId": "aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7",
        "vendorData": null,
        "checkType": "updated_result",
        "matchStatus": "possible_match",
        "searchTerm": {
            "name": "Mirko Kokki",
            "year": "1960"
        },
        "totalHits": 1,
        "createdAt": "2021-07-05T13:23:59.851Z",
        "hits": [{
            "matchedName": "Miro kokkino",
            "countries": [
                "Australia",
                "Brazil"
            ],
            "dateOfBirth": "1963",
            "dateOfDeath": null,
            "matchTypes": [
                "aka_exact",
                "year_of_birth"
            ],
            "aka": [
                "Mirkoni kokki",
                "Mirkor Kokki"
            ],
            "associates": [
                "Desmon Lamela",
                "Fred Austin"
            ],
            "listingsRelatedToMatch": {
                "warnings": [{
                    "sourceName": "FBI Most Wanted",
                    "sourceUrl": "http://www.fbi.gov/wanted",
                    "date": null
                }],
                "sanctions": [{
                    "sourceName": "Argentina Ministerio de Relaciones Exteriores y Culto Sanciones de la ONU",
                    "sourceUrl": "https://www.cancilleria.gob.ar/es/politica-exterior/seguridad-internacional/comite-de-sanciones",
                    "date": null
                }],
                "fitnessProbity": [],
                "pep": [{
                    "sourceName": "United Kingdom Insolvency Service Disqualified Directors",
                    "sourceUrl": "https://www.insolvencydirect.bis.gov.uk/IESdatabase/viewdirectorsummary-new.asp",
                    "date": null
                }],
                "adverseMedia": [{
                    "sourceName": "SNA's Old Salt Award Passed to Adm. Davidson",
                    "sourceUrl": "https://www.marinelink.com/amp/news/snas-old-salt-award-passed-adm-davidson-443093",
                    "date": null
                }]
            }
        }]
    }
}

failed response

{
    "status": "fail",
    "code": "1818",
    "message": "Signature \"334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14\" does not match the HMAC-SHA256 of query ID and integration API secret."
}

DELETE /sessions/{sessionId}

Method to request deletion of a session, a session is eligible for deletion on created, started, abandoned, expired, approved, resubmission_requested, declined, inflow_completed, review statuses. Attempting to delete the session in other statuses will respond with the Session is not in a completed status. error message. The availability of this feature is optional, depending on integration.

If session status is one of the following created, started or resubmission_requested a decision webhook with expired/abandoned status will be sent. After successful request, session will immediately become unavailable in Station and API. Data will be deleted within 12 hours.
Rate-limit is: 10 sessions per 24 hours and 5 sessions per 1 hour.

Request method: DELETE
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Session ID signed with API Private Key
Content-Type: application/json

Sample REQUEST

curl

curl -X DELETE \
  --url '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0'

Node.js

var request = require('request');

var options = { method: 'DELETE',
  url: '/v1/sessions/fd5c1563-1d23-4b1a-ae46-7ba429927ed8',
  headers: {
     'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY'
  },
 };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/sessions/{sessionId}'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0',
    'Content-Type': 'application/json'
}

response = requests.request('DELETE', url, headers=headers)
pprint.pprint(response.json())

Response

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Success response

Response HTTP status code: 202 Accepted
Response body:

{
    "status": "success",
    "verification": {
        "id": "fd5c1563-1d23-4b1a-ae46-7ba429927ed8"
    }
}
Failed responses

Response HTTP status code: 400 Bad Request
Response body:

{
    "status": "fail",
    "code": "1305",
    "message": "Session is not in a completed status."
}

Response HTTP status code: 400 Bad Request
Response body:

{
    "status": "fail",
    "code": "1306",
    "message": "Session in progress."
}
Limited access response

Response HTTP status code: 429 Too Many Requests
Response body:

{
    "status": 429,
    "code": "1004",
    "message": "Too many requests",
}

GET /attempts/{attemptId}/media

Get the list of media objects with attemptId = {attemptId}

If fetched within 7 days of session submit, the list of objects will be sorted by most relevant-> least relevant.

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Attempt ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/attempts/f5c68aea-7f4d-478d-80ab-ca9356074f69/media' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/attempts/f5c68aea-7f4d-478d-80ab-ca9356074f69/media',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/attempts/f5c68aea-7f4d-478d-80ab-ca9356074f69/media'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Get the media with attemptId = {attemptId}

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample RESPONSE

{
  "status": "success",
  "videos": [
    {
      "context": "{VIDEO_TYPE}",
      "id": "{MEDIA_ID}",
      "name": "{VIDEO_NAME}",
      "duration": "{DURATION_IN_SECONDS}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "timestamp": "null",
      "size": "{SIZE_IN_B}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ],
  "images": [
    {
      "context": "{IMAGE_TYPE}",
      "id": "{MEDIA_ID}",
      "name": "{IMAGE_NAME}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "timestamp": "null",
      "size": "{SIZE_IN_B}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ]
}

GET /media/{mediaId}

Get the media with mediaId = {mediaId}

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Media ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/media/ebbf6434-3bf1-4020-8ac3-1ef51a25c673' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/media/ebbf6434-3bf1-4020-8ac3-1ef51a25c673',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests

url = '/v1/media/ebbf6434-3bf1-4020-8ac3-1ef51a25c673'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
print(response.content)

Response

Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Response body signed with API Private key
TRANSFER-ENCODING: string (required) - Form of encoding used to safely transfer the payload body.

Sample RESPONSE Transfer-Encoding: chunked

Sample chunked data handling /media/{mediaId}

Sample how to store response data to file (image or video)

Headers
X-AUTH-CLIENT: string (required) - API Public Key X-HMAC-SIGNATURE: string (required) - Media ID signed with API Private key Media ID: string (required) - Media ID which can be parsed from /media response by session or attempt

Node.js

Here is a simple example how to download media with Node.js , it is applicable for both video and image files . Just make sure to use correct file extension which can be found from /media api response

const fs = require('fs');
const request = require('request');

var options = { method: 'GET',
  url: '/v1/media/ebbf6434-3bf1-4020-8ac3-1ef51a25c673',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);
}).pipe(fs.createWriteStream(__dirname+'/myMedia.jpeg'));

Video files sample

media_url = '/v1/media/05cfc122-15d8-4838-bbf1-7b26a736b2d2'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'Content-Type': 'application/json',
}

response = requests.get(media_url, headers=headers)

with open('media_filename.webm', 'wb') as media_file:
    for chunk in response.iter_content():
        media_file.write(chunk)

Image files sample

media_url = '/v1/media/2b3b3a9f-d73d-445a-aabe-9b41c1c1a2ac'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'Content-Type': 'application/json',
}

response = requests.get(media_url, headers=headers)
with open('image_file_name.jpg', 'wb') as write_file:
    write_file.write(response.content)

GET /transportation-registry/{attemptId}

Get the list of transportation registry results with attemptId = {attemptId}

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Attempt ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/transportation-registry/f5c68aea-7f4d-478d-80ab-ca9356074f69' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36' \

Node.js

var request = require('request');

var options = {
  method: 'GET',
  url: '/v1/transportation-registry/f5c68aea-7f4d-478d-80ab-ca9356074f69',
  headers:
   {
     'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY'
  }
};

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/transportation-registry/f5c68aea-7f4d-478d-80ab-ca9356074f69'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Get the media with attemptId = {attemptId}

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample RESPONSE

{
  "rightToDrive": {
    "attemptId": "e30122d1-740b-4764-853f-470374a7abf4",
    "reason": "Expired right to drive",
    "status": "invalid",
    "validUntil": "2016-08-31",
  },
}

GET /attempts/{attemptId}/proof-of-address

Get the proof of address check result for an attempt with attemptId = {attemptId}.

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Attempt ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/attempts/55d3a307-f799-44d7-aab2-0de70ebe1f2c/proof-of-address' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/attempts/55d3a307-f799-44d7-aab2-0de70ebe1f2c/proof-of-address',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/attempts/55d3a307-f799-44d7-aab2-0de70ebe1f2c/proof-of-address'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Get the proof of address check result for an attempt with attemptId = {attemptId}.

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample RESPONSE

{
  "status": "success",
  "data": {
    "attemptId": "55d3a307-f799-44d7-aab2-0de70ebe1f2c",
    "status": "success",
    "created": "2022-08-16T07:45:07.627Z",
    "updated": "2022-08-16T07:45:07.627Z",
    "name": "Jane Doe",
    "address": "Jaan Koorti 777, 99999 Tallinn, Estonia",
    "parsedAddress": {
      "city": "Tallinn",
      "state": "Harju maakond",
      "line1": "777 Jaan Koorti",
      "line2": "Lasnamäe linnaosa",
      "country": "Estonia",
      "postCode": "99999",
    },
    "dateOfIssue": "2022-10-05",
    "nameConfidence": 0.96,
    "nameMatched": true,
    "addressConfidence": 0.93,
    "documentConfidence": 62,
    "documentType": "invoice"
  }
}

GET /address/{addressId}/media

Get the list of media objects with addressId = {addressId} for proof of address sessions.

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Address ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/address/f087f21f-5282-41b8-9857-6f85c28b8122/media' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/address/f087f21f-5282-41b8-9857-6f85c28b8122/media',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests
import json
import pprint

url = '/v1/address/f087f21f-5282-41b8-9857-6f85c28b8122/media'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
pprint.pprint(response.json())

Response

Get the media with attemptId = {attemptId}

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private key
Content-Type: application/json

Sample RESPONSE

{
  "status": "success",
  "images": [
    {
      "context": "{IMAGE_TYPE}",
      "id": "{MEDIA_ID}",
      "name": "{IMAGE_NAME}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "timestamp": "null",
      "size": "{SIZE_IN_B}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ]
}

GET /address-media/{mediaId}

Get the media for proof of address with mediaId = {mediaId}

Request method: GET
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Media ID signed with API Private key
Content-Type: application/json

curl

curl -X GET \
  --url '/v1/address-media/8d79522f-e3ad-4c1f-8e6a-4248db6935a2' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -H 'X-HMAC-SIGNATURE: 452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b' \

Node.js

var request = require('request');

var options = { method: 'GET',
  url: '/v1/address-media/8d79522f-e3ad-4c1f-8e6a-4248db6935a2',
  headers:
   { 'Content-Type': 'application/json',
     'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
     'X-AUTH-CLIENT': 'API-PUBLIC-KEY' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

Python3.py

import requests

url = '/v1/address-media/8d79522f-e3ad-4c1f-8e6a-4248db6935a2'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'Content-Type': 'application/json'
}

response = requests.request('GET', url, headers=headers)
print(response.content)

Response

Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Response body signed with API Private key
TRANSFER-ENCODING: string (required) - Form of encoding used to safely transfer the payload body.

Sample RESPONSE Transfer-Encoding: chunked

Sample chunked data handling /address-media/{mediaId}

Sample how to store response data to file (image or video)

Headers X-AUTH-CLIENT: string (required) - API Public Key X-HMAC-SIGNATURE: string (required) - Media ID signed with API Private key

Node.js

Here is a simple example how to download address media with Node.js. Just make sure to use correct file extension which can be found from /media api response

const fs = require('fs');
const request = require('request');

var options = {
  method: 'GET',
  url: '/v1/address-media/8d79522f-e3ad-4c1f-8e6a-4248db6935a2',
  headers: {
    'Content-Type': 'application/json',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY'
  }
};

request(options, function (error, response, body) {
  if (error) throw new Error(error);
}).pipe(fs.createWriteStream(__dirname + '/myMedia.jpeg'));

Image files sample

media_url = '/v1/address-media/8d79522f-e3ad-4c1f-8e6a-4248db6935a2'

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'X-HMAC-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
    'Content-Type': 'application/json',
}

response = requests.get(media_url, headers=headers)
with open('image_file_name.jpg', 'wb') as write_file:
    write_file.write(response.content)

POST /validate-registry

Validates a national ID number (e.g. social security number (SSN)) with the provided data. A verification session will be automatically created and submitted after which the validation result gets sent via Webhook. Note that media upload is not used for registry verification.

In order to perform a social security number (SSN) validation the following fields must be provided:

Request method: POST
Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Available request properties:

Sample Request

curl

curl -X POST \
  --url '/v1/validate-registry/' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: API-PUBLIC-KEY' \
  -d '{
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith",
            "idNumber": "123456789",
            "dateOfBirth": "1980-03-06"
        },
        "address": {
            "street": "Street 123",
            "houseNumber": "456"
        },
        "vendorData": "11111111",
        "timestamp": "2016-05-19T08:30:25.597Z"
    }
}'

Node.js

var request = require('request');

var options = { method: 'POST',
    url: '/v1/validate-registry/',
    headers:
            { 'Content-Type': 'application/json',
                'X-AUTH-CLIENT': 'API-PUBLIC-KEY' },
    body:
            { verification:
                        { callback: 'https://veriff.com',
                            person:{
                                firstName: 'John',
                                lastName: 'Smith',
                                idNumber: '123456789',
                                dateOfBirth: "1980-03-06"
                            },
                            address: {
                                street: 'Street 123',
                                houseNumber: '456'
                            },
                            vendorData: '11111111',
                            timestamp: '2016-05-19T08:30:25.597Z' } },
    json: true };

request(options, function (error, response, body) {
    if (error) throw new Error(error);

    console.log(body);
});

Python3

import requests
import pprint
import json

url = '/v1/validate-registry/'

payload = json.dumps({
    'verification': {
        'callback': 'https://veriff.com',
        'person': {
            'firstName': 'John',
            'lastName': 'Smith',
            'idNumber': '123456789',
            'dateOfBirth': '1980-03-06'
        },
        'address': {
            'street': 'Street 123',
            'houseNumber': '456',
        },
        'vendorData': '11111111',
        'features': [
          'selfid'
        ],
        'timestamp': '2016-05-19T08:30:25.597Z'
    }
})

headers = {
    'X-AUTH-CLIENT': 'API-PUBLIC-KEY',
    'Content-Type': 'application/json'
}

response = requests.request('POST', url, data=payload, headers=headers)
pprint.pprint(response.json())

Response

Response Properties explained:

Media type: application/json
Type: object
Headers
X-AUTH-CLIENT: string (required) - API Public Key
X-HMAC-SIGNATURE: string (required) - Request body signed with API Private Key
Content-Type: application/json

Sample RESPONSE

{
    "status": "success",
    "verification": {
        "id":"f04bdb47-d3be-4b28-b028-a652feb060b5",
        "vendorData": "11111111",
        "status": "submitted",
        "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214"
    }
}

X-HMAC-SIGNATURE

Signing requests

It is important to check that the webhook responses do indeed originate from Veriff. For that we use the X-HMAC-SIGNATURE header, which value is an HMAC-SHA256 hex encoded keyed hash using your API private key.

You can secure the webhook listener in three ways:

How to make sure the request to your endpoint originates from Veriff:

Generating X-HMAC-SIGNATURE

Here are few code examples how to sign the request.

Example verification payload:

In case of a GET request, where there is no payload in the body and just URL parameters, for example GET "/transportation-registry/123456, the payload to be signed will be the id from the URL as shown below:

payloadAsString="123456"

A second example, for POST /sessions, in this case the body includes a json payload:

payloadAsString="{"verification":{"callback":"https://veriff.com","person":{"firstName":"John","lastName":"Smith"},"document":{"type":"PASSPORT","country":"EE"},"vendorData":"unique id of a user","timestamp":"2016-05-19T08:30:25.597Z"}}"

The result of the generated hash should be 6af6d95822e19e9cc707aec55395d8d363ba2c7bc4625bc04ebeca0c7bf8cd67 using code examples below with the API private key abcdef12-abcd-abcd-abcd-abcdef012345.

JavaScript / ECMAScript

const signature = crypto
  .createHmac('sha256', apiPrivateKey)
  .update(Buffer.from(payloadAsString, 'utf8'))
  .digest('hex')
  .toLowerCase();

console.log(signature);

Python

signature = hmac \
    .new(api_secret, msg=payloadAsString, digestmod=hashlib.sha256) \
    .hexdigest() \
    .lower()

print(signature)

PHP

<?php
  $signature = strtolower(hash_hmac('sha256', $payloadAsString, $apiPrivateKey));
  echo $signature;

C# / .Net

var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(api_private_key));
var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(payloadAsString));
var signature = Convert.ToHexString(hash).ToLower();

Console.WriteLine(signature);

Using online tooling

Use your favorite HMAC-SHA256 calculator to calculate the hash. E.g. https://codebeautify.org/hmac-generator (link opens in new window)

Signing your request with headers

To make sure you are sending correct HTTP headers, here is the list of the headers and their values to compose the correct request.

CONTENT-TYPE: application/json
X-AUTH-CLIENT: abcdef12-abcd-abcd-abcd-abcdef012345
X-HMAC-SIGNATURE: 6af6d95822e19e9cc707aec55395d8d363ba2c7bc4625bc04ebeca0c7bf8cd67

Validating X-HMAC-SIGNATURE

We sign all responses using the same logic so you can make sure that the response is sent by Veriff using the X-HMAC-SIGNATURE header.

Here are few examples how to validate the response signature.

JavaScript / ECMAScript

function isSignatureValid({ signature, apiPrivateKey, payload }) {
  if (payload.constructor === Object) {
    payload = JSON.stringify(payload);
  }

  if (payload.constructor !== Buffer) {
    payload = new Buffer.from(payload, 'utf8');
  }

  const digest = crypto
    .createHmac('sha256', apiPrivateKey)
    .update(Buffer.from(payload, 'utf8'))
    .digest('hex')
    .toLowerCase();

  return digest === signature.toLowerCase();
}

Testing the validation functionality

The easiest way to test the validation functionality is to setup small service in your localhost and use cURL with the correct payload to test it. This way you can test your functionality independently from our Webhook or API service.

Step 1 - Setup a simple HTTP server with a simple POST endpoint

Small JavaScript / ECMAScript code example

import express from 'express';
import bodyParser from 'body-parser';
import { createHmac } from 'crypto';
import { Server } from 'http';

const app = express();

const SERVICE_PORT = 3001;
const API_SECRET = 'abcdef12-abcd-abcd-abcd-abcdef012345';

function isSignatureValid({ signature, apiPrivateKey, payload }) {
    if (payload.constructor === Object) {
        payload = JSON.stringify(payload);
    }

    if (payload.constructor !== Buffer) {
        payload = new Buffer.from(payload, 'utf8');
    }

    const digest = createHmac('sha256', apiPrivateKey)
        .update(Buffer.from(payload, 'utf8'))
        .digest('hex')
        .toLowerCase();

    return digest === signature.toLowerCase();
}

app.use(bodyParser.json());
let server = Server(app);

app.post('/verification/', (req, res) => {
    res.json({
        isSignatureValid: isSignatureValid({
            signature: req.get('x-hmac-signature'), apiPrivateKey: API_SECRET, payload: req.body
        })
    });
})

server.listen(SERVICE_PORT, () => console.log('Server is UP \n Listening port:', SERVICE_PORT));

Step 2 - Post a prepared data into HTTP server

The curl request described below will return {"isSignatureValid":true} if you use abcdef12-abcd-abcd-abcd-abcdef012345 as an API private key to validate the signature.

curl --request POST 'http://localhost:3001/verification/' -k \
--header 'accept:application/json' \
--header 'x-auth-client:abcdef12-abcd-abcd-abcd-abcdef012345' \
--header 'x-hmac-signature:6af6d95822e19e9cc707aec55395d8d363ba2c7bc4625bc04ebeca0c7bf8cd67' \
--header 'content-type:application/json' \
--data '{"verification":{"callback":"https://veriff.com","person":{"firstName":"John","lastName":"Smith"},"document":{"type":"PASSPORT","country":"EE"},"vendorData":"unique id of a user","timestamp":"2016-05-19T08:30:25.597Z"}}'

Attempts

/attempts /{attemptId}/media (GET)

Get the list of media objects with attemptId = {attemptId}

Request

URI Parameters attemptId: string (required)

Response

Properties

{
  "status": "success",
  "videos": [
    {
      "id": "{MEDIA_ID}",
      "name": "{VIDEO_NAME}",
      "duration": "{DURATION_IN_SECONDS}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "timestamp": {
        "url": "{GUARDTIME_TIMESTAMP_URL}",
        "id": "{TIMESTAMP_ID}"
      },
      "size": "{SIZE_IN_KB}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ],
  "images": [
    {
      "id": "{MEDIA_ID}",
      "name": "{IMAGE_NAME}",
      "url": "{MEDIA_DOWNLOAD_URL}",
      "timestamp": {
        "url": "{GUARDTIME_TIMESTAMP_URL}",
        "id": "{TIMESTAMP_ID}"
      },
      "size": "{SIZE_IN_KB}",
      "mimetype": "{MEDIA_MIME_TYPE}"
    }
  ]
}

Media

/media /{mediaId} (GET)

Get the media

Request

Headers

X-AUTH-CLIENT: string (required) API Public Key
X-HMAC-SIGNATURE: string (required) Media ID signed with API Private key
URI Parameters
mediaId: string (required)

Response

Headers

X-AUTH-CLIENT: string (required) API Public Key
X-HMAC-SIGNATURE: string (required) Response body signed with API Private key
TRANSFER-ENCODING: chunked

Response and error codes

Note: If you do not see the reason code you received here, see also granular reason codes

Common response codes
Code Description
200 `{ "status": "success", "data" }`
201 `{ "status": "success", "data" }`
400 `{ "status": "fail", "code:": "1102", "message": "Mandatory parameters are missing from the request." }`
401 `{ "status": "fail", "message": "Not Authorized." }`
404 `{ "status": "fail", "message": "Entry not found." }`
500 `{ "status": "fail", "message": "Something went wrong." }`
Credentials & Authorization
1801 `Mandatory X-AUTH-CLIENT header containing the API key is missing from the request.`
1802 `API key is not a valid UUID.`
1803 `Integration with the API key was not found.`
1804 `Integration with the API key is not active.`
1812 `Signature is not a valid SHA256 hash.`
1813 `Signature does not match the SHA256 hash of query ID and integration API secret.`
1814 `Signature does not match the SHA256 hash of request body and integration API secret.`
1818 `Signature does not match the HMAC-SHA256 of query ID and integration API secret.`
1819 `Signature does not match the HMAC-SHA256 of request body and integration API secret.`
Some Troubleshooting codes
Code Description
1001 Query ID must be between 20 and 40 symbols.
1002 Query ID must be a valid UUID V4
1003 Query ID must be unique, it has already been used.
1102 Mandatory parameters are missing from the request.
1104 Request includes invalid parameters.
1201 Invalid timestamp. Timestamp must not be older than one hour.
1202 Timestamp format is incorrect. YYYY-MM-DDTHH:MM:S+Timezone Offset|Z or UTC.
1203 Invalid ISO 8601 date. Date needs to be in format YYYY-MM-DD.
1301 Requested features are not supported.
1302 Only HTTPS return URLs are allowed.
1303 Invalid status.
1304 Cannot transition to "$STATUS" status.
1400 Image data not found.
1401 Image is not in valid base64.
1402 Image context is not supported.
1403 Image property is missing.
1500 Vendor data cannot be more than 400 symbols. We require only non-semantic data to be submitted (e.g. UUID-s, etc that can not be resolved or used outside of the vendor environment.
1501 Vendor data must be a string. We require only non-semantic data to be submitted (UUID-s etc that can not be resolved or used outside of the vendor environment).
2003 Date of birth is not a valid date.
2101 Document number has to be between 6 and 9 characters.
2102 Document number may contain only characters and numbers A-Z, 0-9.
2103 Document type is not supported.
2104 Document from provided country is not supported.
Reason and Decision codes
Decline verification.reasonCode
Code Description
102 Suspected document tampering.
103 Person showing the document does not appear to match document photo.
105 Suspicious behaviour.
106 Known fraud.
108 Velocity/abuse duplicated user.
109 Velocity/abuse duplicated device.
110 Velocity/abuse duplicated ID.
112 Restricted IP location
Resubmit verification.reasonCode
Code Description
201 Video and/or photos missing.
204 Poor image quality.
205 Document damaged.
206 Document type not supported.
207 Document expired.
Decision verification.code
Code Description
9001 Positive: Person was verified. The verification process is complete. Accessing the sessionURL again will show the client that nothing is to be done here.
9102 Negative: Person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. You should investigate the session further and read the "reason". If you decide to give the client another try you need to create a new session.
9103 Resubmitted: Resubmission has been requested. The verification process is not completed. Something was missing from the client and she or he needs to go through the flow once more. The same sessionURL can and should be used for this purpose.
9104 Negative: Verification has been expired. The verification process is complete. After 7 days the session gets expired. If the client started the verification process we reply "abandoned" here, otherwise if the client never arrived in our environment the status will be "expired"
9121 Review: Review status is issued whenever automation engine could not issue a conclusive decision and the verification session needs to be reviewed by a human. This status will be sent depending on service agreement.

Granular reason codes

The reason codes mentioned here are subject to custom configuration in integration level, so please keep in mind that, you may or may not receive all the reason codes listed here .

Special Reason codes
Decline verification.reasonCode
Code Description
106 Known fraud
108 Velocity/abuse duplicated user
109 Velocity/abuse duplicated device
110 Velocity/abuse duplicated ID
112 Restricted IP location
502 Multiple parties present in session
503 Attempted deceit
504 Attempted deceit, device screen used
505 Attempted deceit, printout used
507 Presented document tampered, data cross reference
508 Presented document tampered, document similarity to specimen
509 Person showing the document does not match document photo
515 Attempted deceit, device screen used for face image
526 Attempted deceit, photos streamed
527 Unable to collect proof of address data
528 Proof of address issue date too old
Resubmit verification.reasonCode
Code Description
602 Presented document type not supported
603 Video missing
605 Face image missing
606 Face is not clearly visible
608 Document front missing
609 Document back missing
614 Document front not fully in frame
615 Document back not fully in frame
619 Document data not visible
620 Presented document expired
621 Document annulled or damaged
625 Unable to collect surname
626 Unable to collect first names
627 Unable to collect date of birth
628 Unable to collect issue date
629 Unable to collect expiry date
630 Unable to collect gender
631 Unable to collect document number
632 Unable to collect personal number
633 Unable to collect nationality
634 Unable to collect home address
635 Document and face image missing

Biometric authentication

Prerequisites

  1. An IDV integration and an authentication integration set up, connected to one another. (has to be done by your dedicated team at Veriff)
  2. Once the integrations are set up and connected, in order to enroll the user to the biometric authentication product, it is required that the user has an approved IDV session from the IDV integration, where the vendorData field is populated with a uuid, unique to each user that has been verified.

Process overview

Verify the user’s identity

Using your configured IDV integration, verify the user’s identity. (Integrations reference)
vendorData field must be set to the unique user UUID (see above).
With the integrations being connected, face images of approved sessions through the IDV integration (identified with vendorData field) will be automatically added to the set of approved faces, which can be used for future authentications.

Authenticate the user

This is applicable once the user has an approved session containing a populated vendorData with their unique UUID:

  1. Generate an authentication session using the Authentication integration tokens. (Check /sessions endpoint in the API reference here).
      a. Use the vendorData field to pass the same user identifier used in the IDV session
  2. Choose face capture method:
      a. Capturing images with Veriff’s SDKs
      i. Send the end user through the verification flow to capture the new face image using one of Veriff’s SDKs (iOS, Android, React Native, Flutter, inContext). You will need the session url generated at step 1 to use the SDKs.
      Note: Session will be submitted automatically once the user takes necessary images of their face.
      b. Your own face capturing method (not recommended)
      i. Upload the user's face image via the POST media endpoint. Specify context parameter as face.
      ii. Patch session status to submitted status using PATCH /sessions/{sessionId} endpoint.
  3. Receive the results from Veriff via webhooks.

Handling decisions

verification.status verification.code What to do
approved 9001 Positive: Person was verified. The verification process is complete. Accessing the sessionURL again will show the client that nothing is to be done here.
declined 9102 Negative: Person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. It could also have been that due to a bad image quality the person could not been matched. If you decide to give the client another try you need to create a new session.
expired 9104 Negative: Verification has been expired. The verification process is complete. After the session creation, if the end user has not started the verification in 7 days, the session gets expired.
abandoned Negative: Verification has been abandoned. The verification process is complete. After the session creation, if the end user has started but not completed the verification in 7 days, the session gets abandoned status.

Handling the decision reasons

verification.status verification.reasonCode verification.reason
declined 105 Suspicious behaviour
declined 120 Person on the portrait does not appear to match reference photo
expired null null
abandoned

Authentication Service Flow Diagram

biometric-authentication-flow

Face Match (Deprecated)

Prerequisites

  1. You should have a document-front image of the user that needs re-authentication. This image will be passed to the verification session via API as a reference to compare the newly captured “face” image to.
  2. A Face-match integration should be configured. (Reach out to your contact at Veriff to set it up for you!)

Process overview

  1. Generate a session using the Face-match integration tokens. Check /sessions endpoint in the API reference here.
    Note: You can use vendorData parameter to pass a unique identifier for the user who will be verified. We require only non-semantic data to be submitted (e.g. UUID-s, etc that can not be resolved or used outside of the vendor environment).
  2. Upload the reference image (document-front). Check /sessions/{sessionId}/media POST endpoint here.
    Note: Set context parameter as document-front while using POST media endpoint.
  3. Choose face capture method:
      a. Capturing images with Veriff’s SDKs
      i. Send the end user through the verification flow to capture the new face image using one of Veriff’s SDKs (iOS, Android, React Native, Flutter, inContext). You will need the session url generated at step 1 to use the SDKs.
      Note: Session will be submitted automatically once the user takes necessary images of their face.
      b. Your own face capturing method (not recommended)
      i. Upload the user's face image via the POST media endpoint. Specify context parameter as face.
      ii. Patch session status to submitted status using PATCH /sessions/{sessionId} endpoint.
  4. Receive the results from Veriff via webhooks.

Handling decisions

verification.status verification.code What to do
approved 9001 Positive: Person was verified. The verification process is complete. Accessing the sessionURL again will show the client that nothing is to be done here.
declined 9102 Negative: Person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. You should investigate the session further and read the "reason". If you decide to give the client another try you need to create a new session.
resubmission_requested 9103 Resubmitted: Resubmission has been requested. The verification process is not completed. Something was missing from the client and they need to go through the flow once more. The same sessionURL can and should be used for this purpose. And the reference image should be uploaded again (same if related to the face, different one if related to the reference image)
expired 9104 Negative: Verification has been expired. The verification process is complete. After the session creation, if the end user has not started the verification in 7 days, the session gets expired.
abandoned Negative: Verification has been abandoned. The verification process is complete. After the session creation, if the end user has started but not completed the verification in 7 days, the session gets abandoned status.

Handling the decision reasons

verification.status verification.reasonCode verification.reason What to do
declined 509 Person showing the document does not match document photo User’s new face doesn’t match with reference uploaded image
declined 515 Attempted deceit, device screen used for face image Face image shown from a device/screen/printout.
declined 526 Attempted deceit, photos streamed Face images are being presented as a slide show/stream.
Note: This feature comes when using Veriff's end user flow to capture the images
declined 106 Known fraud Suspicious multi/country accesses, previously declined multiple times for fraud
declined 108 Decline - Velocity/abuse duplicated user This feature needs to be enabled for your integration. It is not active by default
declined 109 Decline - Velocity/abuse duplicated device This feature needs to be enabled for your integration. It is not active by default
declined 110 Decline - Velocity/abuse duplicated ID This feature needs to be enabled for your integration. It is not active by default
resubmission_requested 605 Face missing from image
  1. Upload reference image again to the same session uuid
  2. Send the end user to the same url.
resubmission_requested 606 Face is not clearly visible
resubmission_requested 608 Document front missing
  1. Reference image/s not good enough to do face match, try uploading a different document-front image.
  2. Send the user to the same url.
resubmission_requested 619 Document data not visible
expired null null Create a new session for the user
abandoned

Face Match Service Flow Diagram

face-match-flow

Webhooks

To handle the response from Veriff services, you will need to implement an endpoint that accepts payloads posted by our services. Please note that Veriff does not allow custom ports to be added to the webhook URLs.

Configuring the webhook endpoint

Go to Veriff Station, Integrations -> Find the integration to configure -> Settings, and set one of the webhook URLs to the URL where your endpoint is accepting payloads from Veriff. There are several types of webhooks.
Veriff will post payloads to respective webhook URLs.

If there is a network connectivity issue or technical issue with delivering the notification (any non-200 response code), Veriff will retry the notification once in every hour for up to a week.

Recognizing your customer

When your server receives a payload from Veriff, you need to be able to reference a customer.
There are a couple of ways to do this.

Using the Veriff session ID

The easiest way is to track the session ID provided by Veriff during session creation. All future webhooks payloads refer to the attempt ID. The attempt ID is unique for each session and it can be used to look up sessions in Station interface.

Using your own customer ID

To use your own customer ID you need to provide your internal customer ID to Veriff, or some other key that uniquely identifies your customer. You can store your identifier in the vendorData property during the session creation. Please bear in mind that it is technically possible for one customer to be associated with multiple verification sessions, and this could potentially create ambiguous situations in code, if you are only recognizing customers by your own identifier, and not Veriff's session ID.

Handling security

It is important to check that the webhook responses do indeed originate from Veriff. For that we use the X-HMAC-SIGNATURE header, which value is an HMAC-SHA256 hex encoded keyed hash using your API private key.

Different webhook types

There are several different webhook types to help you to tailor your service specifically to your needs.

Meaning of the various verification responses

Verification status is one of

Verification response code is one of 9001, 9102, 9103, 9104, 9121 All codes and explanations can be found from Response and error codes

Explanation of the meaning of the response codes: Meaning of the various event codes

Code Description
9001 Positive: Person was verified. The verification process is complete. Accessing the sessionURL again will show the client that nothing is to be done here.
9102 Negative: Person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. You should investigate the session further and read the "reason". If you decide to give the client another try you need to create a new session.
9103 Resubmitted: Resubmission has been requested. The verification process is not completed. Something was missing from the client and she or he needs to go through the flow once more. The same sessionURL can and should be used for this purpose.
9104 Negative: Verification has been expired. The verification process is complete. After 7 days the session gets expired. If the client started the verification process we reply "abandoned" here, otherwise if the client never arrived in our environment the status will be "expired".
9121 Review: Review status is issued whenever automation engine could not issue a conclusive decision and the verification session needs to be reviewed by a human. This status will be sent depending on service agreement.

Lifecycle of the verification session

Responses 9001, 9102, 9121 and 9104 are conclusive responses. The session is closed and the URL is not available for the end user.

Response 9103 is an inconclusive response. The session remains open until you receive one of the above conclusive responses. The session is re-opened for the end user, accepting a new attempt.

Preconditions for approval decisions

We give a positive conclusive decision (status approved, code 9001) when the user has provided us with:

Accessing the KYC session URL again will tell * the user that nothing more is to be done.

Reasons for negative conclusive decisions

A negative decision means that the person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. You should investigate the session further and read the "reason". If you decide to give the client another try you need to create a new session.

Reasons for inconclusive decisions

*In case of verifications which have received a "Resubmission requested" decision from Veriff, we highly recommend notifying the user about the reason why the verification did verification fail and tips for what could be done better on the next attempt. This can be done either via e-mail, SMS and/or in your platform for better visibility. In case of resubmitted verification attempts where something was missing or the quality of the submission was poor, a next attempt is created ready to be used and accessible via the same session URL. The same session URL should be used for verification attempts by the same user.

Final decisions

You have the right to make the final decision about verifying an end-user even after they have completed the verification process with Veriff. In order for you to make the final decision, we recommend you create an appropriate functionality in your back-end. This will enable you to either approve or reject applications after the person has completed Veriff’s verification session. When you perform additional checks outside of Veriff’s service, it is necessary to have another layer of decision making. The decision has to be motivated based on further checks or extra information independent of Veriff’s decision. The additional verification details can be determined in accordance with your business needs and the amount of data and background information available to you about the end-user.

Potential use cases:

Meaning of the various event codes

{
  "id": "f04bdb47-d3be-4b28-b028-a652feb060b5",
  "feature": "selfid",
  "code": 7002,
  "action": "submitted",
  "vendorData": "QWE123"
}

The event webhook informs you about the progress of the verification or proof of address data gathering. However, it does not inform you of the decision.

The event code can be one of:

For verification:

For proof of address:

Storing verification media

For businesses which are regulated by various KYC and AML laws and rules, storing proof of the customer's verification is often a requirement. While Veriff stores and holds the customer's media - photos and videos - it might also be mandatory and more convenient for the business to store this media internally.

First, you need to get the sessionId of a session you want to download the media for. This can be found from the decision webhook(parameter "id") which is automatically sent after a decision has been made for a verification session. With the sessionID, make a GET request to /sessions/{sessionId}/media endpoint. From there, you will find a mediaId for every media file we have stored.

With the mediaIds, you can make a GET request to /media/{mediaId} endpoint to get the media file in .jpeg or .mp4 format. A separate request will have to be made for each media file.

Automating the download of photo and video files associated with completed verifications.

/sessions/{sessionId}/media First, query for a list of files using a GET request to /media/{mediaId} The response to your GET request to /sessions{sessionID}/media will be a list of videos and images related to this sessionID.

Second, the individual media files can be downloaded by using the mediaID returned in the first step with a GET request to /media/{mediaID}

Decision webhook

This is the description of the payload sent to Webhook decisions URL.
The result of the verification is sent back to the vendor once the verification has been processed.

In most cases we send decision webhook instantly after decision is made with an exception of "resubmission_requested" status. In case resubmission is required we allow end user to resubmit session data instantly without a need to exit the flow. If end user does't do it within 5 minutes we'll send out webhook with resubmission_requested decision.

For the Driver License extraction of category 'valid from' and 'valid until' dates please communicate with your account manager to activate the feature.

Properties

{
  "status": "success",
  "verification": {
    "id": "12df6045-3846-3e45-946a-14fa6136d78b",
    "code": 9001,
    "person": {
      "gender": null,
      "idNumber": null,
      "lastName": "MORGAN",
      "addresses": [
        {
          "fullAddress": "1234 Snowy Ridge Road, Indiana, 56789 USA",
          "parsedAddress": {
            "city": null,
            "unit": null,
            "state": "Indiana",
            "street": "1234 Snowy Ridge Road",
            "country": "USA",
            "postcode": "56789",
            "houseNumber": "null"
          }
        }
      ],
      "firstName": "SARAH",
      "citizenship": null,
      "dateOfBirth": "1967-03-30",
      "nationality": null,
      "yearOfBirth": "1967",
      "placeOfBirth": "MADRID",
      "pepSanctionMatch": null
    },
    "reason": null,
    "status": "approved",
    "comments": [],
    "document": {
      "type": "DRIVERS_LICENSE",
      "number": "MORGA753116SM9IJ",
      "country": "GB",
      "validFrom": null,
      "validUntil": "2022-04-20",
      "placeOfIssue": "MADRID",
      "firstIssue": "2015-03-21",
      "issueNumber": "01",
      "issuedBy": "ISSUER"
    },
    "reasonCode": null,
    "vendorData": "12345678",
    "decisionTime": "2019-11-06T07:18:36.916Z",
    "acceptanceTime": "2019-11-06T07:15:27.000Z",
    "additionalVerifiedData": {
      "driversLicenseCategory": {
        "B": true
      },
      "driversLicenseCategoryFrom": {
        "B": "2019-10-06"
      },
      "driversLicenseCategoryUntil": {
        "B": "2025-10-05"
      }
    },
    "riskLabels": [
    {
      "label": "document_integration_level_crosslinked_with_fraud",
      "category": "document",
      "sessionIds": ["5a2358e7-fd31-4fcb-a23f-4d76651ba68a"]
    },
    {
      "label": "document_integration_level_crosslinked_with_multiple_declines",
      "category": "document",
      "sessionIds": ["fd5c1563-1d23-4b1a-ae46-7ba429927ed8"]
    }]
  },
  "technicalData": {
    "ip": "186.153.67.122"
  }
}

Properties explained:

Event webhook

This is the description of the payload sent to Webhook events URL.

We can get two types of events:

Verification events

To keep the clients up to date with progress during the verification process Veriff allows to subscribe to certain events. Currently two events are triggered:

Properties

{
  "id": "f04bdb47-d3be-4b28-b028-a652feb060b5",
  "attemptId": "e30122d1-740b-4764-853f-470374a7abf4",
  "feature": "selfid",
  "code": 7002,
  "action": "submitted",
  "vendorData": "QWE123"
}

Properties explained:

Proof of address events

To track the progress of proof of address data gathering, Veriff allows to subscribe to the following events:

Properties

{
  "id": "f04bdb47-d3be-4b28-b028-a652feb060b5",
  "addressId": "f087f21f-5282-41b8-9857-6f85c28b8122",
  "feature": "selfid",
  "code": 7005,
  "action": "submitted",
  "vendorData": "QWE123"
}

Properties explained:

Watchlist screening webhook

This is the description of the payload sent to Webhook watchlist screening URL.
The response of the check is sent back to the client once the check is complete or an ongoing monitoring check comes in.

Properties

{
  "checkType": "updated_result",
  "attemptId": "54233318-f81c-4ec4-8e4c-413168a3f5e6",
  "vendorData": "12345678",
  "matchStatus": "possible_match",
  "searchTerm": {
    "name": "Mirko Kokki",
    "year": "1960"
  },
  "totalHits": 5,
  "createdAt": "2021-06-02T11:04:00.287Z",
  "hits": [
    {
      "matchedName": "Miro kokkino",
      "countries": [
        "Australia",
        "Brazil"
      ],
      "dateOfBirth": "1960",
      "dateOfDeath": null,
      "matchTypes": [
        "aka_exact"
      ],
      "aka": [
        "Kokki Mirko",
        "Mirko Kokki"
      ],
      "associates": [
        "Desmon Lamela",
        "Fred Austin"
      ],
      "listingsRelatedToMatch": {
        "warnings": [
          {
            "sourceName": "FBI Most Wanted",
            "sourceUrl": "http://www.fbi.gov/wanted",
            "date": null
          }
        ],
        "sanctions": [
          {
            "sourceName": "Argentina Ministerio de Relaciones Exteriores y Culto Sanciones de la ONU",
            "sourceUrl": "https://www.cancilleria.gob.ar/es/politica-exterior/seguridad-internacional/comite-de-sanciones",
            "date": null
          },
          {
            "sourceName": "Argentina Public Registry of People and Entities linked to acts of Terrorism and Terrorism Financing",
            "sourceUrl": "https://repet.jus.gob.ar/#entidades",
            "date": null
          }
        ],
        "fitnessProbity": [
          {
            "sourceName": "United Kingdom Insolvency Service Disqualified Directors",
            "sourceUrl": "https://www.insolvencydirect.bis.gov.uk/IESdatabase/viewdirectorsummary-new.asp"
          }
        ],
        "pep": [
          {
            "sourceName": "United States Navy Leadership and Senior Military Officials",
            "sourceUrl": "https://www.navy.mil/Leadership/Biographies/"
          }
        ],
        "adverseMedia": [
          {
            "date": "2020-09-23T00:00:00Z",
            "sourceName": "SNA's Old Salt Award Passed to Adm. Davidson",
            "sourceUrl": "https://www.marinelink.com/amp/news/snas-old-salt-award-passed-adm-davidson-443093"
          }
        ]
      }
    }
  ]
}

Properties explained:

Transportation registry webhook

This is the description of the payload sent to Webhook transportation registry URL.
The response of the check is sent back to the client once the check is completed.

Properties

{
  "eventType": "transportation_registry_check.verified",
  "eventCode": 7006,
  "details": {
    "rightToDrive": {
      "attemptId": "e30122d1-740b-4764-853f-470374a7abf4",
      "reason": "Expired right to drive",
      "status": "invalid",
      "validUntil": "2016-08-31"
    }
  },
  "sessionId": "03c4070b-fb7a-4c8d-8619-bf1145ecaece",
  "vendorData": "Custom vendor provided information"
}

Properties explained:

Proof of address webhook

This is the description of the payload sent to Webhook proof of address URL.
The response of the check is sent back to the client once the check is completed.

Properties

{
  "eventCode": 9201,
  "eventType": "proof_of_address_success",
  "version": "1.0.0",
  "created": "2022-08-24T19:50:10.664Z",
  "sessionId": "125c0472-ebae-4cc9-a0de-bd6574c6bfc1",
  "vendorData": null,
  "details": {
    "attemptId": "55d3a307-f799-44d7-aab2-0de70ebe1f2c",
    "status": "success",
    "created": "2022-08-16T07:45:07.627Z",
    "updated": "2022-08-16T07:45:07.627Z",
    "name": "Jane Doe",
    "address": "Jaan Koorti 777, 99999 Tallinn, Estonia",
    "parsedAddress": {
      "city": "Tallinn",
      "state": "Harju maakond",
      "line1": "777 Jaan Koorti",
      "line2": "Lasnamäe linnaosa",
      "country": "Estonia",
      "postCode": "99999"
    },
    "dateOfIssue": "2022-10-05",
    "documentType": "invoice",
    "nameConfidence": 0.96,
    "nameMatched": true,
    "addressConfidence": 0.93,
    "documentConfidence": 62
  }
}

Properties explained:

Testing

For testing purposes we provide an example payload so you do not depend on Veriff sending the response. You can use this Curl command. Beware, that the signature will not match your API Public Key and API Private Key. To validate this example signature and payload use API Private Key 'abcdef12-abcd-abcd-abcd-abcdef012345'.

curl --request POST 'https://your.url' -k \
--header 'accept:application/json' \
--header 'x-auth-client:8e4f7cd8-7a19-4d7d-971f-a076407ee03c' \
--header 'x-hmac-signature:e2bab556b6f6e9ddeb9f61071b8f243680335ed091f9cb3d51fe308711093343' \
--header 'content-type:application/json' \
--data '{"status":"success","verification":{"id":"12df6045-3846-3e45-946a-14fa6136d78b","code":9001,"person":{"gender":null,"idNumber":null,"lastName":"MORGAN","addresses":[{"fullAddress":"1234  Snowy Ridge Road, Indiana , 56789"}],"firstName":"SARAH","citizenship":null,"dateOfBirth":"1967-03-30","nationality":null,"yearOfBirth":"1967","placeOfBirth":"MADRID","pepSanctionMatch":null},"reason":null,"status":"approved","comments":[],"document":{"type":"DRIVERS_LICENSE","number":"MORGA753116SM9IJ","country":"GB","validFrom":null,"validUntil":"2022-04-20","placeOfIssue":"MADRID","firstIssue":"2015-03-21","issueNumber":"01","issuedBy":"ISSUER"},"reasonCode":null,"vendorData":"12345678","decisionTime":"2019-11-06T07:18:36.916Z","acceptanceTime":"2019-11-06T07:15:27.000Z","additionalVerifiedData":{"driversLicenseCategory":{"B":true},"driversLicenseCategoryFrom":{"B":"2019-10-06"},"driversLicenseCategoryUntil":{"B":"2025-10-05"}},"riskLabels":[{"label":"document_integration_level_crosslinked_with_fraud","category":"document"},{"label":"document_integration_level_crosslinked_with_multiple_declines","category":"document"}]},"technicalData":{"ip":"186.153.67.122"}}'

Response and error codes

Note: If you do not see the reason code you received here, see also granular reason codes

Common response codes
Code Description
200 `{ "status": "success", "data" }`
201 `{ "status": "success", "data" }`
400 `{ "status": "fail", "code:": "1102", "message": "Mandatory parameters are missing from the request." }`
401 `{ "status": "fail", "message": "Not Authorized." }`
404 `{ "status": "fail", "message": "Entry not found." }`
500 `{ "status": "fail", "message": "Something went wrong." }`
Credentials & Authorization
1801 `Mandatory X-AUTH-CLIENT header containing the API key is missing from the request.`
1802 `API key is not a valid UUID.`
1803 `Integration with the API key was not found.`
1804 `Integration with the API key is not active.`
1812 `Signature is not a valid SHA256 hash.`
1813 `Signature does not match the SHA256 hash of query ID and integration API secret.`
1814 `Signature does not match the SHA256 hash of request body and integration API secret.`
1818 `Signature does not match the HMAC-SHA256 of query ID and integration API secret.`
1819 `Signature does not match the HMAC-SHA256 of request body and integration API secret.`
Some Troubleshooting codes
Code Description
1001 Query ID must be between 20 and 40 symbols.
1002 Query ID must be a valid UUID V4
1003 Query ID must be unique, it has already been used.
1102 Mandatory parameters are missing from the request.
1104 Request includes invalid parameters.
1201 Invalid timestamp. Timestamp must not be older than one hour.
1202 Timestamp format is incorrect. YYYY-MM-DDTHH:MM:S+Timezone Offset|Z or UTC.
1203 Invalid ISO 8601 date. Date needs to be in format YYYY-MM-DD.
1301 Requested features are not supported.
1302 Only HTTPS return URLs are allowed.
1303 Invalid status.
1304 Cannot transition to "$STATUS" status.
1400 Image data not found.
1401 Image is not in valid base64.
1402 Image context is not supported.
1403 Image property is missing.
1500 Vendor data cannot be more than 400 symbols. We require only non-semantic data to be submitted (e.g. UUID-s, etc that can not be resolved or used outside of the vendor environment.
1501 Vendor data must be a string. We require only non-semantic data to be submitted (UUID-s etc that can not be resolved or used outside of the vendor environment).
2003 Date of birth is not a valid date.
2101 Document number has to be between 6 and 9 characters.
2102 Document number may contain only characters and numbers A-Z, 0-9.
2103 Document type is not supported.
2104 Document from provided country is not supported.
Reason and Decision codes
Decline verification.reasonCode
Code Description
102 Suspected document tampering.
103 Person showing the document does not appear to match document photo.
105 Suspicious behaviour.
106 Known fraud.
108 Velocity/abuse duplicated user.
109 Velocity/abuse duplicated device.
110 Velocity/abuse duplicated ID.
112 Restricted IP location
Resubmit verification.reasonCode
Code Description
201 Video and/or photos missing.
204 Poor image quality.
205 Document damaged.
206 Document type not supported.
207 Document expired.
Decision verification.code
Code Description
9001 Positive: Person was verified. The verification process is complete. Accessing the sessionURL again will show the client that nothing is to be done here.
9102 Negative: Person has not been verified. The verification process is complete. Either it was a fraud case or some other severe reason that the person can not be verified. You should investigate the session further and read the "reason". If you decide to give the client another try you need to create a new session.
9103 Resubmitted: Resubmission has been requested. The verification process is not completed. Something was missing from the client and she or he needs to go through the flow once more. The same sessionURL can and should be used for this purpose.
9104 Negative: Verification has been expired. The verification process is complete. After 7 days the session gets expired. If the client started the verification process we reply "abandoned" here, otherwise if the client never arrived in our environment the status will be "expired"
9121 Review: Review status is issued whenever automation engine could not issue a conclusive decision and the verification session needs to be reviewed by a human. This status will be sent depending on service agreement.

Granular reason codes

The reason codes mentioned here are subject to custom configuration in integration level, so please keep in mind that, you may or may not receive all the reason codes listed here .

Special Reason codes
Decline verification.reasonCode
Code Description
106 Known fraud
108 Velocity/abuse duplicated user
109 Velocity/abuse duplicated device
110 Velocity/abuse duplicated ID
112 Restricted IP location
502 Multiple parties present in session
503 Attempted deceit
504 Attempted deceit, device screen used
505 Attempted deceit, printout used
507 Presented document tampered, data cross reference
508 Presented document tampered, document similarity to specimen
509 Person showing the document does not match document photo
515 Attempted deceit, device screen used for face image
526 Attempted deceit, photos streamed
527 Unable to collect proof of address data
528 Proof of address issue date too old
Resubmit verification.reasonCode
Code Description
602 Presented document type not supported
603 Video missing
605 Face image missing
606 Face is not clearly visible
608 Document front missing
609 Document back missing
614 Document front not fully in frame
615 Document back not fully in frame
619 Document data not visible
620 Presented document expired
621 Document annulled or damaged
625 Unable to collect surname
626 Unable to collect first names
627 Unable to collect date of birth
628 Unable to collect issue date
629 Unable to collect expiry date
630 Unable to collect gender
631 Unable to collect document number
632 Unable to collect personal number
633 Unable to collect nationality
634 Unable to collect home address
635 Document and face image missing

AML Screening

PEP & Sanction screening with IDV

PEP & Sanctions, adverse media screening together with identity verification

Prerequisites

An IDV integration with PEP & Sanctions checks configured together with optional adverse media checks and ongoing monitoring. This has to be done by your dedicated team at Veriff.

Process overview

  1. Once the integration is set up, generate a verification session using the regular /sessions end-point.
  2. After the end-user has submitted the verification flow and the session has been verified, the AML result will be sent in a separate webhook. You can also query for the result using the watchlist-screening API end-point
  3. In case ongoing monitoring is enabled, we will send a new webhook if the PEP & Sanctions status of a verified person has changed.
    a. The Webhook PEP & Sanctions URL can be configured from the “Integrations” menu

PEP & Sanction screening without IDV

PEP & Sanctions, adverse media screening without identity verification

Prerequisites

A non-IDV integration with PEP & Sanctions checks configured together with optional adverse media checks and ongoing monitoring. This has to be done by your dedicated team at Veriff.

Process overview

  1. Generate a new verification session using the /sessions end-point and specify the following fields in the request:
    a. First name
    b. Last name
    c. Date of Birth
  2. Change the status of the session to “Submitted” using PATCH /sessions/{sessionId} end-point
  3. The results will be sent in a separate webhook and are also available using the /watchlist-screening end-point.
    a. The Webhook PEP & Sanctions URL can be configured from the “Integrations” menu