NAV Navbar

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 Settings 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.

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",
        "lang": "en",
        "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://stationapi.veriff.com/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:

Generating the X-SIGNATURE header

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

The X-SIGNATURE is a SHA-256 hex encoded hash of the concatenation of the request body and your API secret.

To generate the X-SIGNATURE header, please:

For example, if the request object is

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

Next, take the request object and add your API-Secret to the end.(replace 'Your-API-Secret' with your API secret)

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

Alternatively, if you use stringified request body then your concatenated payload will look as following.(replace 'Your-API-Secret' with your API secret)

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

Now you will need to calculate a SHA256 hash and hex encode it, as the description of that calculation is in the API spec at #sessions

This process depends on the language you use.

Using JavaScript / ECMA

To view the code sample select the language at the top-right

const payload = JSON.stringify(verification);
const signature = crypto.createHash('sha256');
signature.update(new Buffer(payload, 'utf8'));
signature.update(new Buffer(secret, 'utf8'));
return signature.digest('hex');

Using C# / .Net

To view the code sample select the language at the top-right

string hashString;
using (var sha256 = SHA256Managed.Create())
{
    var hash = sha256.ComputeHash(Encoding.Default.GetBytes(payloadPlusSecret));
    hashString = ToHex(hash, false);
}

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

iOS

Android

Integrations

Android SDK integration

Android SDK Requirements

Veriff Android SDK requires Android 5.0 or newer at runtime. The project must also have Java 8 enabled and use AndroidX instead of support library.

Adding 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/" } //veriff
        google()
        jcenter()
    }
}

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

implementation 'com.veriff:veriff-library:3.1.1'

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.VeriffSdk;

Intent intent = VeriffSdk.createLaunchIntent(context, sessionUrl);
startActivityForResult(intent, REQUEST_CODE);

Parameters are defined as below

Parameters Description
sessionUrl 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)

Starting from version 2.6.0 you can customize the look and feel of the SDK flow by passing a VeriffBranding instance via VeriffConfiguration to createLaunchIntent. See the example below.

VeriffBranding branding = new VeriffBranding.Builder()
        .themeColor(getResources().getColor(R.color.theme_color))
        .toolbarIcon(R.drawable.toolbar_icon)
        .notificationIcon(R.drawable.notification_icon)
        .build();

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


Intent intent = VeriffSdk.createLaunchIntent(context, sessionUrl, configuration);
startActivityForResult(intent, REQUEST_CODE);

The theme color, toolbar and notification icons are optional. If a value is not provided for them the default Veriff theme color, and icons will be used.

Setting a locale for the SDK (Optional)

Starting from version 3.1.0 you can set a locale(java.util.Locale) for the SDK from the app itself. Even if you set a locale for the SDK the users will still be able to change the language during the flow if they wish to.

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

Intent intent = VeriffSdk.createLaunchIntent(context, 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) {
        VeriffResult result = VeriffResult.fromResultIntent(data);
        if (result != null) {
            handleResult(result); //see below for handling the result
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

public void handleResult(VeriffResult 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;
    }
}

Note: After the verification data is uploaded, the SDK does not wait for the final verification result. The SDK only notifies whether the verification data upload was successful or not. The verification result is sent to the vendor`s server asynchronously in the background. (Reference for that can be found here).

Adding error logging

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

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

Migrating to Veriff Android SDK 3.0.0

Follow these steps to migrate from SDK 2.x.y 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

Android SDK Changelog

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

Versions
    Release 3.1.1
    • Fixed a thread and file descriptor leak.
    • Technical optimisations and bug fixes.
    Release 3.1.0
    • Added support for specifying a language by passing a Locale when launching the SDK.
    • Fixed a crash when attempting to continue the flow without selecting a country.
    • Fixed flow getting stuck if exit dialog was displayed during the initial session load.
    Release 3.0.1
    • Added an option to have a selfie-pic-only flow.
    Release 3.0.0
    • Breaking: Veriff Android SDK now requires AndroidX and Java8.
    • Revamped the API of the SDK and deprecated the old API
    Release 2.13.0
    • Improved messaging when reopening already completed sessions.
    • Veriff resource IDs no longer show up in the IDE auto-complete window.
    • Improved accessibility with various display zoom levels and screen readers.
    • Fixed receiving feedback at the end of the flow.
    • The document selection screen now specifically mentions "government ID".
    • Fixed translation issues with Serbian (Latin) in various places.
    • Fixed a crash with the combination of Android 10, `targetSdkVersion 29` and `READ_PHONE_STATE` permission.
    Release 2.12.0
    • Added biometric verification flow for supported passports.
    • Moved selfie step after the document steps.
    • Improved assisted image capture.
    • Technical optimisations and bug fixes.
    Release 2.11.3
    • Improved translations and copy.
    Release 2.11.2
    • Improved UI of the 'leave user waiting' feature.
    • Fixed background video corruption on the Google Pixel 3 family.
    • Technical optimizations and bugfixes
    Release 2.11.1
    • Added 'leave user waiting' feature.
    • Fixed a crash after session submission with residence permits.
    Release 2.11.0
    • Improved efficiency and performance of image uploads
    • Updated handover screen design
    • Added a confirmation dialog when leaving the upload screen
    • Added an option to retry failed uploads at the end of the flow
    Release 2.10.0
    • Fixed language change for some host apps
    • Fixed an occasional crash after scanning the barcode of a US driver's license
    Release 2.9.0
    • Improved file uploads
    • Improved error reporting and analytics
    Release 2.8.0
    • Added support for additional image quality checks
    • Fixed a crash on Android 5.x when host app uses AndroidX Appcompat 1.1.x
    Release 2.7.1
    • Fixed a critical ClassNotFound regression introduced in 2.7.0
    Release 2.7.0 (Deprecated due critical build issues)
    • Updated the look and feel of the SDK
    • Fixed unclickable shutter button when capturing before camera has loaded
    • Fixed duplicate flow if internet was lost in the photo preview screen
    • Improved stability and reliability
    Release 2.6.2
    • Added support for white labelling Veriff
    • Added ability to disable Selfie step
    • Pause WebRTC capturing when UI is paused
    • Fixed a crash on camera focusing on some Samsung phones
    Release 2.6.1
    • Fixed a crash when changing language on Android 6.x or older
    Release 2.6.0
    • Added US driver's license barcode scanning
    • Added support for customizing the UI look by changing the main color, toolbar and notification icon
    • Added Spanish (Latin America) localization
    Release 2.5.0
    • Added automatic face detection and focusing on face
    • Changed the copy to be specific to every document type
    • Reduced the file size of uploaded selfie and document images
    • Added 11 new localizations
    • Fixed bugs and increased stability of the SDK
    Release 2.4.8
    • Added support for additional image quality checks
    • Fixed language change from Spanish to Mexican Spanish
    • Fixed "service did not then call startForeground" crashes
    • Fixed video not working with the combination of Veriff, old Gradle plugin and OkHttp 4
    Release 2.4.7
    • Added support for Mexican Spanish
    • Removed white screen as flash while taking selfie
    • WebRTC updates and improvements
    Release 2.4.6
    • Improved downstream proguard rules
    • Fixed SDK crashes after process recreate
    • Fixed a large number of bugs concerning uploads
    • Fixed restarting the verification from the "No internet" error screen
    Release 2.4.5
    • Fixed dark room warning not being translated
    Release 2.4.4
    • Added Japanese language
    • Added dark room detection
    • Fixed a few issues around ending the video session
    Release 2.4.3
    • New intro screen outlining the verification flow
    • Added Turkish language
    • Fixed videos being broken from Samsung 6.x-7.x devices
    • Fixed a few crashes
    • Fixed Veriff title bar clashing with system statusbar on some Android versions
    • Fixed country list keyboard behaviour on Android 5.0
    Release 2.4.2
    • Fixed an issue with capturing photos on Android 5.x devices
    Release 2.4.1
    • Added encryption for photos in internal storage
    • Moved Probity collector into SDK, removing the external dependency on it
    • Camera/webrtc fixes and improvements
    • Various small fixes and improvements
    Release 2.4.0
    • Increased minSdkVersion to 21
    • Added video mandatory flag
    • Increased socket write timeout when uploading
    • Added Arabic support
    • Other camera/webrtc fixes and improvements
    Release 2.3.6
    • Added new languages: Georgian, Hindi, Malay, Ukranian
    • Fixed multiple copy issues
    Release 2.3.5
    • Fixed WebRtc client issues
    • Fixed OnePlus 6T focusing issue
    • Added option to enable verbose webrtc logging
    • Other camera/webrtc fixes and improvements
    • Language fixes for cancel dialog and instructions screen
    Release 2.3.4
    • Country list is now translated
    • Fixed a lot of camera issues on various devices
    • Fixed "Internet connection lost" popping up after verification flow was completed
    • Fixed country selection screen being partially translated
    • Updated translation
    Release 2.3.2
    • Critical hot fix for 2.3.1
    Release 2.3.1
    • Removed setting custom color schema and background image
    • Add proguard rule to keep WebRTC classes and methods (Fixes #6)
    • Added scroll view to document selection screen to fix UI issue in phones with small form factor
    • Bugfixes
    Release 2.3.0
    • Added support for Inflow Feedback
    • Added permission for foreground service
    • Fixed camera issues with Pixel phones
    • Bundled proguard rules with the SDK
    • Fixed aspect ratio issue for legacy flow
    • Added logging for WebRTC errors
    Release 2.2.1
    • Added support for preselected country and document (Fixes #3)
    Release 2.1.1
    • WebRTC production ready
    • Match camera and image preview scaling (camera preview was zoomed in)
    • Focus camera on screen touch
    • Fix video rotation in Backoffice
    • Event tracking for business funnel
    • Implemented in-house crash reporting
    • Improved performance while taking snapshots
    • Always take and upload 2 pictures per step
    • Added Czech and Lithuanian language support
    Release 2.1.0.
    • WebRTC beta support with a fallback
    • Event tracking for business funnel
    • Implemented crash reporting
    • Improved performance while taking snapshots
    Release 2.0.0.
    • New design
    • Integration changes
    • Reduced library size
    • Optimizations
    Release 1.6.8.
    • Fixed camera issue with older Android versions
    Release 1.6.7.
    • Fixed English langauge resource
    • Added gradle publish script
    Release 1.6.6.
    • Added Italian, Dutch and French languages
    Release 1.6.5.
    • Removed Firebase dependency requirement
    • Removed Twilio dependency requirement
    • Removed Eventbus dependecy requirement
    Release 1.6.4.
    • Added missing translations
    Release 1.6.3.
    • Fixed indefinite upload bug
    • Updated language resources
    • New versioning management
    • Upgraded gradle version
    • Upgraded dependecy versions
    Release 1.6.2.
    • Fixed language persistence issue
    Release 1.6.1.
    • Further improved Latvian language resources
    Release 1.6.0.
    • New language selection
    • New color schema
    • Improved face detection
    • Improved Latvian language resources
    • Refactoring and general bug fixes
    Release 1.5.0.
    • New library wide toolbar with cancelation and language selection options
    • Changed text values and removed unused resources
    • Design improvements
    • Refactoring and general bug fixes
    Release 1.4.0.
    • Implemented new Error screens for:
      • Network error
      • System error
      • Uploading error
      • Session expired error
    • Fixed and improved Samsung phones camera issues
    • Improved logging
    • General bug fixes

iOS SDK integration

iOS SDK Requirements

Integration Veriff iOS SDK requires at least iOS version 11.0

Add framework to a project

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

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"
binary "https://cdn.veriff.me/ios/carthage/VeriffWebRTC.json" == 1.0.1

After a carthage update add Veriff and WebRTC frameworks from Carthage/Build/<platform> to project. To target build phases carthage copy-frameworks run script step add the following input files:

$(SRCROOT)/Carthage/Build/iOS/Veriff.framework
$(SRCROOT)/Carthage/Build/iOS/WebRTC.framework

To target build phases carthage copy-frameworks run script step add the following output files:

$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Veriff.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/WebRTC.framework

Using XCFramework

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

After files are downloaded, drag VeriffSDK.xcframework and WebRTC.framework into the Frameworks, Libraries, and Embedded Content section of your target and that's all!

Using the Veriff iOS SDK

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 and microphone permissions for capturing photos an video during identification. Your application is responsible to describe the reason why camera and microphone is used. You must add 2 descriptions listed below to info.plist of your application with the explanation of the usage.

Import Veriff in your code

import Veriff

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

Configure SDK before displaying it

Setting configuration parameters [Required]

var conf = VeriffConfiguration(sessionToken: token, sessionUrl: "https://alchemy.veriff.com")
var veriff = Veriff.shared
veriff.set(configuration: conf)

In order to start verification flow, you must configure the VeriffSDK first, to do that please call the method below with the token you receive from your backend implementation.

User Interface customization

You can customize Veriff user interface via your own application, by letting is know of your brand main color and logo.

See Veriff SDK customization guide document on how it looks.

let yourColor = UIColor.someColor()
let yourImage = UIImage(named: NavigationBarImageName.png)
conf.branding = Branding(themeColor: yourColor, navigationBarImage: yourImage)

Veriff iOS SDK allows the customization of theme color and navigation bar title image with Branding struct.

In order to use customization, please set the branding property of VeriffConfiguration before you start the verification.

Handling result codes from SDK

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

extension VerificationService: VeriffDelegate {
    func onSession(result: VeriffResult, sessionToken: String) {
         switch result.code {
             case .STATUS_DONE:
                // code
             case .STATUS_ERROR_SESSION:
                // code
             ...
             ...
         }
    }
}

Note: To receive status updates, you must implement VeriffDelegate and assign it to delegate property of Veriff instance.

 veriff.delegate = self

Here is list of all possible status codes

Start verification process

veriff.startAuthentication()

Alternatively you can pass a view controller that is used for presenting the verification UI. Please be aware that the view controller has to be in view hierarchy and not presenting.

startAuthentication(from viewController: UIViewController)

Veriff SDK looks for currently presented UIViewController from key UIWindows' root view controller. Using this view controller verification UI is presented.

iOS SDK Changelog

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

Versions

    2.9.4 (Deprecated due critical upload bug)
    • Technical optimisations and bug fixes.
    2.9.3
    • Accessibility support.
    • Selfie only flow support.
    • iOS 13 UIWindow fixes.
    • Fixed info text for microphone permission callback.
    2.9.2
    • XCFramework support
    • Removed SocketIO and Starscream dependencies.
    2.9.1
    • Updated ID translations.
    • User interface improvements on Document Selection screen.
    • Privacy policy link now respects UI language.
    2.9.0
    • Assisted Image Capture
    • Handling of expired tokens improved.
    • Connection timeouts increased.
    • UI fixes on Upload and Language views.
    • Light detection and language change crashes are fixed.
    2.8.4
    • Fix for scaling issue of navigation bar logo.
    • WebRTC connection improvements.
    • Terms of Service text updated.
    2.8.3
    • Network timeout improvements.
    • Xcode 11.4 support.
    2.8.2
    • onSession delegate is called only once.
    2.8.0
    • Animated instruction added for document back pictures.
    • Analytics and error logging improved.
    • Camera refactored.
    2.7.4
    • Update brand
    • Turn barcode scan frame green on success
    • Fix privacy label hiding bug
    • Fix Selfie step appearing in handover screen when portrait picture is disabled
    • Fix language selector not opening over error screens
    • Fix image upload error reporting.
    2.7.3
    • XCode 11.3 support.
    2.7.1
    • Image quality is increased.
    2.7.0
    • Re-architectured SDK
    • Carthage support
    • White label support
    • Preselected document copy fix
    • Preselected text field now clears if user wants to change manually
    • All steps(selfie, document front, document back, passport) are optional now
    • Serbian-Latin language fix
    • Dropped iOS 10 support
    • Reachability removed (a dependency)
    • WebRTC stall error fixed
    2.6.2
    • Fix of AppStore submission warning about es-latam language file.
    • Barcode reading improvements.
    • WebRTC improvements for iOS 13.
    • 3rd party dependency Presentr removed.
    • Low light detection UI fixes.
    2.6.1
    • Xcode 11.2.1 & Swift 5.1.2 support.
    • WebRTC improvements for iOS 13.
    2.6.0
    • Added US driver's license barcode scanning.
    • Added support for 11 new languages.
    2.5.2
    • Added support for Mexican Spanish.
    • Removed white screen as flash while taking selfie.
    • Session validity token implemented.
    2.5.1
    • Privacy policy web pages localized.
    • Performance & Analytics improvements.
    2.5.0
    • Support for changing UI colors.
    • Japanese language support.
    • Performance & Analytics improvements.
    2.4.2
    • UI improvements.
    • Turkish language support.
    • Performance & Analytics improvements.
    2.4.1
    • Camera freezeing when coming from background fixed.
    • Enhanced logging output.
    2.4.0
    • Video mandatory added.
    • RTL support added.
    2.3.4
    • Use correct strings for instruction view subtitles
    • Remove 3rd party code from repo
    2.3.3
    • Tap to focus camera
    • Camera started log event added
    • Preselected document string change
    • User can change country and document
    2.3.2
    • Country list is now translated
    • Updated translations
    • Device info logging
    2.3.0
    • Added support for preselected country and document (Fixes #2)
    • New languages added: Czech and Lithuanian
    • Bitcode support
    2.2.0
    • WebRTC video(bitcode support will follow in 2.2.1)
    2.1.0
    • WebRTC beta support(face detection and bitcode support will follow in 2.2.1)
    2.0.5
    • Xcode 10.2 support
    2.0.4
    • Improving uploading conversions for devices on slow networks
    • Fallback to device language if session is missing language
    • Minor UI changes
    2.0.3
    • Restores Xcode debugging support
    • Bugfixes
    2.0.2
    • Navigation bar appearance issues fixed
    2.0.1
    • Restored support for i386 x86_64 architectures
    2.0.0
    • New design
    • Added country selection
    • Integration changes
    • Reduced library size
    • Optimizations
    1.6.6
    • New languages added: Dutch, French, Italian
    1.6.5
    • Removes video call functionality
    1.6.4
    • New languages added: Chinese, Polish, Portuguese, Spanish, Vietnamese
    • Translations updated for existing languages
    1.6.3
    • Fixes storage wipe issue on cancel verification
    • Updates Latvian and Russian translations
    1.6.2
    • Fixes issue with method name collision
    1.6.1
    • Minor design changes
    • Fixes crash on upload
    1.6.0
    • New language switcher design
    • Updates to default colors in UI
    1.5.2
    • Latvian translations updated
    1.5.1
    • General bugfixes
    1.5.0
    • New library wide toolbar with cancelation and language selection options
    • Changed text values and removed unused resources
    • Design improvements
    • Camera stability improvements
    • Refactoring and general bug fixes
    1.4.0
    • Improved camera stability
    • Improved logging
    • Updated Latvian translations
    • General bug fixes

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.

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()
    }
}

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 both use_frameworks! and use_native_modules! directives at the end:

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

  use_frameworks!
  use_native_modules!
end

Note: If your project is using Swift 5.1 you need to use a Swift 5.1 compatible version of the Veriff SDK. Add the following line to Podfile:

target 'MyApp' do
  pod 'VeriffSDK', '~> x.y.z-swift51'
  ...
end

Current Swift version can be checked from Terminal with:

xcrun swift -version

Substitute x.y.z with VeriffSDK version you are using.

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 and microphone permissions for capturing photos an video during identification. Your application is responsible to describe the reason why camera and microphone is used. You must add 2 descriptions listed below to Info.plist of your application with the explanation of the usage.

Add them to the ios/TARGET/Info.plist file:

<dict>
...
    <key>NSCameraUsageDescription</key>
    <string>Access to camera is needed for user identification purposes </string>
    <key>NSMicrophoneUsageDescription</key>
    <string>Access to microphone is needed for video identification</string>
</dict>

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 11.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';

Before you can launch the verification flow you'll need a session token. See our documentation on generating one. Once you have the token you can launch Veriff using VeriffSdk imported earlier:

var result = await VeriffSdk.launchVeriff({ sessionToken: TOKEN });

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 theme color and navigation bar title image and notification image (Android only) by passing in these optional properties when launching Veriff:

var result = await VeriffSdk.launchVeriff({
    sessionToken: TOKEN,
    branding: {
      themeColor: '#ff00ff',
      navigationBarImage: 'parrot',
      androidNotificationIcon: 'ic_parrot'
    },
  });

When the 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. 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.

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({ sessionToken: TOKEN });
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;
}

React Native SDK Changelog

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

Versions
    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 your own 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.

  const veriff = Veriff({
    apiKey: 'API_KEY',
    parentId: 'veriff-root',
    onSession: function(err, response) {
      // received the response, verification can be started now
    }
  });

  veriff.mount();

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. 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: 'example@mail.com'
  });
  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: 'Email'
    },
    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.

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 });

createVeriffFrame([options])

Create modal with Veriff application.

Returns an object that controls the modal.

.close()

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.

Adding Content Security Policy to JS SDK

add_header Content-Security-Policy default-src 'self' *.veriff.me; 
script-src 'unsafe-inline' 'unsafe-eval' 'self' *.veriff.me *.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.me *.probity.io; 
style-src 'self' *.veriff.me 'unsafe-inline';

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 delvier an increasingly smarter, better and safer product, it's important to ditch 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:

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

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 'Your-API-Key' and 'Your-API-Token' 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 || 'Your-api-key'; 
const API_SECRET = process.env.API_SECRET || 'Your-api-token';
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.

API Reference

Requirements

API Key and API token can be found by logged in customers on settings page.

Headers

X-AUTH-CLIENT: string (required) - API Key
CONTENT-TYPE: string (required) - Type for the request: (application/json)
X-SIGNATURE: string (required) - Request Body signed with API Secret.

X-Signature is a SHA-256 hex encoded hash. The stringified body is concatenated by the API Secret and then hex digested.

API URL https://stationapi.veriff.com/v1

Note: Headers are sent with each request

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:

/sessions

Request Properties explained:

Note: lang: String - The language of end user verification flow. In case it is sent with API we try to display flow in the predefined language. In case the value is not defined we try to detect browser/device language for flow. In case we do not have translation to defined language we default back to EN.

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

Function to calculate X-Signature in Python samples

Python3

import hashlib

def generateSignature(payload, secret):
    sha_signature = \
        hashlib.sha256(f'{payload}{secret}'.encode()).hexdigest()
    return sha_signature

Sample Request

curl

curl -X POST \
  --url '/v1/sessions/' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: Your-API-KEY' \
  -d '{
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith",
            "idNumber": "123456789"
        },
        "document": {
            "number": "B01234567",
            "type": "PASSPORT",
            "country": "EE"
        },
        "vendorData": "11111111",
        "lang": "en",
        "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': 'Your-api-key' },
  body:
   { verification:
      { callback: 'https://veriff.com',
        person:
         { firstName: 'John',
           lastName: 'Smith',
           idNumber: '123456789' },
        document: { number: 'B01234567', type: 'PASSPORT', country: 'EE' },
        vendorData: '11111111',
        lang: 'en',
        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

API_SECRET = 'Your-api-secret'
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',
        'lang': 'en',
        'features': [
          'selfid'
        ],
        'timestamp': '2016-05-19T08:30:25.597Z'
    }
})

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(payload, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. 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",
    "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214",
    "baseUrl": "https://alchemy.veriff.com"
  }
}

/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 Key X-SIGNATURE: string (required) - Request body signed with API Secret. 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: Your-API-KEY' \
  -H 'X-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-SIGNATURE': 'dd994f70b1150ae012f9c1d6d20adf7ed69780044835d39de20b00ffae0660a0',
     'X-AUTH-CLIENT': 'Your-api-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


API_SECRET = 'Your-api-secret'
url = '/v1/sessions/{sessionId}'

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

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(payload, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. 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"
    }
}

/sessions/{sessionId}/media GET

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

Request method: GET Media type: application/json Type: object Headers X-AUTH-CLIENT: string (required) - API Key X-SIGNATURE: string (required) - Session ID signed with API Secret. 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: Your-API-KEY' \
  -H 'X-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-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py

import requests
import json
import pprint

API_SECRET = 'Your-api-secret'
SESSION_ID=  'Your-session-id'
url = '/v1/sessions/{sessionId}/media'

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(SESSION_ID, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. 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}"
    }
  ]
}

/sessions/{sessionId}/media POST

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 Key X-SIGNATURE: string (required) - Payload signed with API Secret. 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: Your-API-KEY' \
  -H 'X-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-SIGNATURE': '034c6da2bb31fd9e6892516c6d7b90ebe10f79b47cfb3d155d77b4d9b66e1d53',
     'X-AUTH-CLIENT': 'Your-api-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': 'Your-api-key',
    'X-SIGNATURE': generateSignature(payload, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. Content-Type: application/json

Sample RESPONSE

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

/sessions/{sessionId}/person

Request method: GET

Media type: application/json Type: object.

Headers X-AUTH-CLIENT: string (required) - API Key X-SIGNATURE: string (required) - Session ID signed with API Secret. 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: Your-API-KEY' \
  -H 'X-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-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py

import requests
import json
import pprint

API_SECRET = 'Your-api-secret'
SESSION_ID=  'Your-session-id'

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

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(SESSION_ID, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. Content-Type: application/json

Sample RESPONSE

{
    "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, iso2 country code",
        "citizenships": [
            {
                "kind": "string, Main or Secondary",
                "citizenship": "string, country name"
            }
        ],
        "pepSanctionMatches": [
            {
                "numberOfMatches": "number",
                "date": "date with timestamp, 2018-11-28T17:13:28.154Z",
                "matches": [
                    {
                        "name": "string",
                        "nationality": "string",
                        "category": "string, SIP or PEP"
                    },
                ]
            }
        ]
    }
}

/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 Key
X-SIGNATURE: string (required) - Session ID signed with API Secret.
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: Your-api-key' \
  -H 'X-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-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py

import requests
import json
import pprint

API_SECRET = 'Your-api-secret'
SESSION_ID= 'Your-session-id'
url = '/v1/sessions/{sessionId}/attempts'

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(SESSION_ID, API_SECRET),
    '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 Key
X-SIGNATURE: string (required) - Response body signed with API Secret.
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"
      }
  ]
}

/attempts/{attemptId}/media

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

Request method: GET

Media type: application/json Type: object.

Headers
X-AUTH-CLIENT: string (required) - API Key
X-SIGNATURE: string (required) - Attempt ID signed with API Secret.
Content-Type: application/json

curl

curl -X GET \
  --ulr '/v1/attempts/f5c68aea-7f4d-478d-80ab-ca9356074f69/media' \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: Your-api-key' \
  -H 'X-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-SIGNATURE': 'acfe1cf21c986edf25cc6bc74fd769954443bbb606500019a4bed46645179b36',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py

import requests
import json
import pprint

API_SECRET = 'Your-api-secret'
ATTEMPT_ID=  'attemptId'

url = '/v1/attempts/{attemptId}/media'

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(ATTEMPT_ID, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. 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}"
    }
  ]
}

/sessions/{sessionId}/decision

Get the session decision with sessionId = {sessionId}

Request method: GET

Media type: application/json Type: object.

Headers X-AUTH-CLIENT: string (required) - API Key X-SIGNATURE: string (required) - Session ID signed with API Secret. Content-Type: application/json

curl

curl -X GET \
  https://api.veriff.me/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/decision \
  -H 'Content-Type: application/json' \
  -H 'X-AUTH-CLIENT: Your-api-key' \
  -H 'X-SIGNATURE: 334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14' \

Node.js

var request = require("request");

var options = { method: 'GET',
  url: 'https://api.veriff.me/v1/sessions/aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7/decision',
  headers:
   { 'Content-Type': 'application/json',
     'X-SIGNATURE': '334141f052e317fde6668de54dc6640b4a5c47582ad86a8bed63afe566f17b14',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py

import requests
import json
import pprint

API_SECRET = 'Your-api-secret'
SESSION_ID= 'Your-session-id'

url = 'https://api.veriff.me/v1/sessions/{sessionId}/decision'

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(SESSION_ID, API_SECRET),
    '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 Key X-SIGNATURE: string (required) - Response body signed with API Secret. Content-Type: application/json

/media/{mediaId}

Get the media with mediaId = {mediaId}

Request method: GET

Media type: application/json Type: object.

Headers X-AUTH-CLIENT: string (required) - API Key X-SIGNATURE: string (required) - Media ID signed with API Secret. 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: Your-api-key' \
  -H 'X-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-SIGNATURE': '452bfca0e02f8ee0f56d97373cc6971067e43149f1b7e58b681d4e57353a2f6b',
     'X-AUTH-CLIENT': 'Your-api-key' } };

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

  console.log(body);
});

Python3.py


import requests

API_SECRET = 'Your-api-secret'
MEDIA_ID=    'mediaId'

url = '/v1/media/{mediaId}'

headers = {
    'X-AUTH-CLIENT': 'Your-api-key',
    'X-SIGNATURE': generateSignature(MEDIA_ID, API_SECRET),
    'Content-Type': 'application/json'
}

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

Response

Headers X-AUTH-CLIENT: string (required) - API Key X-SIGNATURE: string (required) - Media data signed by API Secret. 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 Key
X-SIGNATURE: string (required) - Media data signed by API Secret.
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 extention which can be found from /media api response

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

const mediaId = 'Your-media-Id';

var options = { method: 'GET',
  url: '/v1/media/'+mediaId,
  headers:
   { 'Content-Type': 'application/json',
     'X-SIGNATURE': 'Calculate sha256 hash with mediaID + YOUR_API_SECRET',
     'X-AUTH-CLIENT': 'YOUR_API_KEY' } };

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

Python3.py

def generateSignature(payload, secret):
    sha_signature = \
        hashlib.sha256(f"{payload}{secret}".encode()).hexdigest()
    return sha_signature

Video files sample

media_id = '05cfc122-15d8-4838-bbf1-7b26a736b2d2'
media_url = f'/v1/media/{media_id}'

headers = {
    "X-AUTH-CLIENT": API_KEY,
    "X-SIGNATURE": generateSignature(media_id, API_SECRET),
    "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_id = '2b3b3a9f-d73d-445a-aabe-9b41c1c1a2ac'
media_url = f'/v1/{media_id}'

headers = {
    "X-AUTH-CLIENT": API_KEY,
    "X-SIGNATURE": generateSignature(media_id, API_SECRET),
    "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)

X-Signature

Signing requests

It is important to check that the webhook responses do indeed originate from Veriff.

You can secure the webhook listener URL in three ways:

have a good secure SSL server for your webhook listener (Veriff will call only to HTTPS URLs, to servers with a publicly verifiable certificate) check the X-AUTH-CLIENT and X-SIGNATURE headers on the decision webhook (the signature is calculated using the API secret that only you and Veriff know) finally, if you are really suspicious, you may restrict your webhook listener to only accept calls from the Veriff IP range (please ask your Veriff integration onboarding specialist for those details).
When Veriff calls your webhook endpoint, we use the same logic of X-SIGNATURE generation on our calls to you, as on your calls to us. So, when we call your endpoint for any notification URL, we set the X-AUTH-CLIENT http header to your API key, and we set the X-SIGNATURE header to the hex encoded sha256 digest of the request body and the secret.

When you accept a webhook call from us, you need to proceed as follows:

Compare the X-AUTH-CLIENT header to your api key, if different -> fail with error access the http request body (before it is parsed) calculate the sha256 digest of the request body string + api secret compare the hex encoded digest with the X-SIGNATURE header, if different -> fail with error only now you should parse the request body JSON object The calculation for X-SIGNATURE is following the same algorithm as for session generation.

Generating X-Signature:

Here are few examples how to do it.

NodeJS

const crypto = require('crypto');

// Globals
const API_KEY = 'Your-api-key';
const API_SECRET = 'Your-api-secret';

const data = 'my precious data';

// Headers 
const headers = 
   { 'Content-Type': 'application/json',
     'X-AUTH-CLIENT': API_KEY,
     'X-SIGNATURE': generateSignature(data, API_SECRET) };


function generateSignature(payload, secret, error) {
  if (payload.constructor === Object) {
    payload = JSON.stringify(payload);
  }

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

  const signature = crypto.createHash('sha256');
  signature.update(payload);
  signature.update(new Buffer.from(secret, 'utf8'));
  return signature.digest('hex');

};

console.log(headers);

Python3


import hashlib
import json

#Globals
API_KEY = 'Your-api-key'
API_SECRET = 'Your-api-secret'
payload=json.dumps({'my precious data':'goes here'})            

def generateSignature(payload, secret): 
  sha_signature = \
      hashlib.sha256(f'{payload}{secret}'.encode()).hexdigest()
  return sha_signature

print(generateSignature(payload, API_SECRET))

Manual Generation:

Create session with following payload via API. Sometimes you might want to use Postman or any other tool for API testing. For this you will need to manually calculate sha-256 hash from payload and send it with X-signature header Keep in mind that if you stringify payload body then you will need to sign stringified payload at hash calculation step as well.

{
    "verification": {
        "callback": "https://veriff.com",
        "person": {
            "firstName": "John",
            "lastName": "Smith",
            "idNumber": "1234567890"
        },
        "document": {
            "number": "B01234567",
            "type": "PASSPORT",
            "country": "EE"
        },
        "vendorData": "11111111",
        "lang": "en",
        "timestamp": "2016-05-19T08:30:25.597Z"
    }
}

First take the payload and add your API-Secret to the end. (replace 'Your-API-Secret' with your API secret) Note: current example can be encoded using "Your-API-Secret" for manual calculation

{
  "verification": {
      "callback": "https://veriff.com",
      "person": {
          "firstName": "John",
          "lastName": "Smith",
          "idNumber": "1234567890"
      },
      "document": {
          "number": "B01234567",
          "type": "PASSPORT",
          "country": "EE"
      },
      "vendorData": "11111111",
      "lang": "en",
      "timestamp": "2016-05-19T08:30:25.597Z"
  }
}Your-API-Secret

Use your favorite SHA-256 hash calculator to calculate hash.
E.g https://xorbin.com/tools/sha256-hash-calculator

SHA-256 hash is: 4f6a803b432dae66340ea7aba816527f0ccac2e8956c3f3db37510eca1177941

X-Signature included to your request headers is above hash:

Headers

Content-Type: application/json  
X-AUTH-CLIENT: Your-API-Key 
X-SIGNATURE: fae37e40c27875f52f944a39d37540d6d32ab4c39bf004c5e107925e44c7c05a

Validating X-Signature:

We sign all responses using same logic. Response body + Your-API-Secret = X-SIGNATURE in response headers.
To be sure that response is sent by Veriff you can validate X-SIGNRATURE.

Here are few examples how to do validate response signature. Below is function you can use.

NodeJS

function isSignatureValid(data) {
  const { signature, secret } = data;
  let { payload } = data;

  if (data.payload.constructor === Object) {
    payload = JSON.stringify(data.payload);
  }
  if (payload.constructor !== Buffer) {
    payload = new Buffer.from(payload, 'utf8');
  }
  const hash = crypto.createHash('sha256');
  hash.update(payload);
  hash.update(new Buffer.from(secret));
  const digest = hash.digest('hex');

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

Validate from response:

Easiest way to test validation function is to setup webhook listnener on localhost and do use cURL for quick validation test. This way you can test your function independently from our Webhook or API responses.

Step 1 - Setup webhook listsener and validate payload.

NodeJS

const crypto = require('crypto');
const request = require("request");
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

const WEBHOOK_PORT = 3001;
const API_SECRET = 'Your-API-Secret';

function isSignatureValid(data) {
  const { signature, secret } = data;
  let { payload } = data;

  if (data.payload.constructor === Object) {
    payload = JSON.stringify(data.payload);
  }
  if (payload.constructor !== Buffer) {
    payload = new Buffer.from(payload, 'utf8');
  }
  const hash = crypto.createHash('sha256');
  hash.update(payload);
  hash.update(new Buffer.from(secret));
  const digest = hash.digest('hex');

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

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

app.post('/verification/', (req, res) => {
  const signature = req.get('x-signature');
  const secret = API_SECRET;
  const payload = req.body;

  console.log('Received a webhook');
  console.log('Is signature valid:', isSignatureValid({ signature, secret, payload }));
  console.log('Payload', JSON.stringify(payload, null, 4));
  res.json({ status: 'success' });
  process.exit();
})

server.listen(WEBHOOK_PORT);
console.log('Server is UP \n Listsening port:', WEBHOOK_PORT);

Step 2 - Post some data to webhook listener for validation:

Below will return: Is signature valid: true if you use 'Your-API-Secret' for validating signature.

CURL

curl --request POST 'https://localhost:3001/verification/' -k \
--header 'accept:application/json' \
--header 'x-auth-client:Your-API-Key' \
--header 'x-signature:cd0e28104516bc27b4506026d81d07e532b927440b84c4f01ac02d92a2b1419c' \
--header 'content-type:application/json' \
--data '{"status":"success","verification":{"id":"aea9ba6d-1b47-47fc-a4fc-f72b6d3584a7","code":9001,"person":{"gender":null,"idNumber":"123456789","lastName":"Smith","firstName":"John","citizenship":null,"dateOfBirth":null,"nationality":null,"yearOfBirth":null,"pepSanctionMatch":null},"reason":null,"status":"approved","comments":[],"document":{"type":"ID_CARD","number":"ee12334","country":"EE","validFrom":null,"validUntil":null},"highRisk":null,"reasonCode":null,"vendorData":"11111111","decisionTime":"2019-10-30T07:32:44.540Z","acceptanceTime":"2019-10-29T12:41:01.000Z","additionalVerifiedData":{}},"technicalData":{"ip":"95.153.29.169"}}'

Manual Validation

All our responses are stringified
Note: current example can be encoded using "Your-API-Secret" for manual calculation

Example response for above:

Headers

Content-Type: application/json  
X-AUTH-CLIENT: Your-API-Key  
X-SIGNATURE: 6b57df1c1284d3bcc24bd49bbac0da323c2380b7d6bcdc1f3d886f6b2ed32a88

Use your favorite SHA-256 hash calculator to calculate hash.
E.g https://xorbin.com/tools/sha256-hash-calculator

{"status":"success","verification":{"id":"d04fd8e7-80cd-4fb8-b235-cec89c5c6790","url":"https://alchemy.veriff.com/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiZDA0ZmQ4ZTctODBjZC00ZmI4LWIyMzUtY2VjODljNWM2NzkwIiwiaWF0IjoxNTcyMjUyODgyfQ.wBEsEAeAzBQamc6acujZoJ4VQxuCsYfwdH8Xe-JI4Qw","vendorData":"11111111","host":"https://front.veriff.me","status":"created","sessionToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiZDA0ZmQ4ZTctODBjZC00ZmI4LWIyMzUtY2VjODljNWM2NzkwIiwiaWF0IjoxNTcyMjUyODgyfQ.wBEsEAeAzBQamc6acujZoJ4VQxuCsYfwdH8Xe-JI4Qw"}}Your-API-Secret

Compare X-Signature in response headers with the result from calculations:

X-SIGNATURE: 6b57df1c1284d3bcc24bd49bbac0da323c2380b7d6bcdc1f3d886f6b2ed32a88
SHA-256 hash: 6b57df1c1284d3bcc24bd49bbac0da323c2380b7d6bcdc1f3d886f6b2ed32a88

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 with mediaId = {mediaId}

Request

Headers

X-AUTH-CLIENT: string (required) Vendor’s API Key X-SIGNATURE: string (required) Media ID signed by Vendor’s API Secret URI Parameters mediaId: string (required)

Response

Headers

X-AUTH-CLIENT: string (required) Vendor’s API Key X-SIGNATURE: string (required) Media data signed by Vendor’s API Secret TRANSFER-ENCODING: string (required) Media data signed by Vendor’s API Secret

Transfer-Encoding: chunked

Response and error 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." }`
Crendentials & 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.`
1811 `Mandatory X-SIGNATURE header containing the SHA256 hash is missing from the request.`
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.`
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.
1501 Vendor data must be a string.
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
101 Physical document not used.
102 Suspected document tampering.
103 Person showing the document does not appear to match document photo.
105 Suspicious behaviour.
106 Known fraud.
107 Velocity/abuse. (Deprecated)
108 Velocity/abuse duplicated user.
109 Velocity/abuse duplicated device.
110 Velocity/abuse duplicated ID.
Resubmit verification.reasonCode
Code Description
201 Video and/or photos missing.
202 Face not clearly visible.
203 Full document not visible.
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"

Veriff Assisted Image Capture API

Process overview

  1. Generate session. Check /sessions endpoint in the API reference here to learn how to generate one.
  2. Upload media
  3. Wait for response
  4. Patch session to inflow_completed

Upload media

Upload media for a specific verification session and receive inflow feedback

URL

/sessions/{sessionId}/media

Method

POST

URI Parameters

sessionId: string (required) - Session uuid

Headers

Content-Type: application/json
X-AUTH-CLIENT: string (required) - API Key
X-SIGNATURE: string (required) - Payload signed with API Secret.

Request Properties

Example request body { "image": { "content": "/9j/4AAQSkZJRgABAQEBLAEsAAD/4R...", "context": "document-back", "timestamp": "2019-10-16T10:19:48.445Z", "inflowFeedback": true } }

RESPONSE

Inflow feedback passed
Happens when no issues were found with the image

Code : 200 OK

{
  "status": "success",
  "image": {
    "id": "be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "name": "document-back",
    "context": "document-back",
    "timestamp": {
      "url": "https://stationapi.veriff.com/v1/timestamps/b65b3628-232c-424a-9277-f3b84b568e82",
      "id": "b65b3628-232c-424a-9277-f3b84b568e82"
    },
    "size": 35406,
    "mimetype": "image/jpeg",
    "url": "https://stationapi.veriff.com/v1/media/be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "sessionId": "7c66bb47-b071-451f-a55b-66a8d125470f",
    "inflowFeedback": {
      "combined": {
        "result": true
      },
      "reasons": []
    }
  }
}

Inflow feedback rejected
Happens when some issues were found with the image

Code : 200 OK

{
  "status": "success",
  "image": {
    "id": "be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "name": "document-back",
    "context": "document-back",
    "timestamp": {
      "url": "https://stationapi.veriff.com/v1/timestamps/b65b3628-232c-424a-9277-f3b84b568e82",
      "id": "b65b3628-232c-424a-9277-f3b84b568e82"
    },
    "size": 35406,
    "mimetype": "image/jpeg",
    "url": "https://stationapi.veriff.com/v1/media/be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "sessionId": "7c66bb47-b071-451f-a55b-66a8d125470f",
    "inflowFeedback": {
      "combined": {
        "result": false
      },
      "reasons": [
        {
          "name": "document_data_readable",
          "result": false
        },
        {
          "name": "document_found",
          "result": false
        },
        {
          "name": "document_valid_position",
          "result": false
        }
      ]
    }
  }
}

Inflow feedback timeout
Happens when we’re not able to give feedback in time

Code : 200 OK

{
  "status": "success",
  "image": {
    "id": "be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "name": "document-back",
    "context": "document-back",
    "timestamp": {
      "url": "https://stationapi.veriff.com/v1/timestamps/b65b3628-232c-424a-9277-f3b84b568e82",
      "id": "b65b3628-232c-424a-9277-f3b84b568e82"
    },
    "size": 35406,
    "mimetype": "image/jpeg",
    "url": "https://stationapi.veriff.com/v1/media/be660c8f-3fc4-49aa-8051-ac042f07d6c7",
    "sessionId": "7c66bb47-b071-451f-a55b-66a8d125470f",
    "inflowFeedback": null
  }
}

List of feedbacks

Priority Suggested user guidance Reason identifier Image type
1 Photo is too dark. Please take a brighter photo to continue. image_not_too_dark Any
2 No ID card/driver's license/residence permit/passport detected. Please check if your card/driver's license/residence permit/passport is valid and in the photo document_found Document
3 ID card/driver's license/residence permit/passport is too small. Try bringing your ID card closer to the camera. document_valid_size Document
4 Make sure your ID card/driver's license/residence permit/passport photo page fits fully in the frame. document_valid_position Document
5 Please use a valid and supported identification document. These include ID cards, passports, residence permits, or driver's licenses. document_valid_type Document
6 Monochrome photo. Please take a color photo of your original ID card/driver's license/residence permit/passport to continue. document_photocopy_not_monochrome Document
7 Your ID card/driver's license/residence permit/passport appears to be a photocopy. Please use the original to continue. document_photocopy Document
8 Take a photo of the front of your ID card/driver's license/residence permit. document_front_shown Document front
9 Take a photo of the back of your ID card/driver's license/residence permit. document_back_shown Document back
10 Camera glare detected. Try moving away from direct light. document_data_glare Document
11 Document data is not readable. Please take a sharper photo to continue. / Make sure there is no not finger covering the data. document_data_readable Document
12 Make sure your face and ID card/driver's license/residence permit/passport photo page are fully in the frame when taking a photo. document_face_shown Face + Document
13 Make sure that your face is in the frame and clearly visible face_found Face
14 Multiple faces detected. Please make sure you are alone in the photo. face_count Face

Patch the session

Patching session to inflow_completed signals Veriff to leave the session alone. Check /sessions/{sessionId} endpoint in the API reference here for more details.

Method

PATCH

URI Parameters

sessionId: string (required) - Session uuid

Headers

Content-Type: application/json
X-AUTH-CLIENT: string (required) - API Key
X-SIGNATURE: string (required) - Payload signed with API Secret.

Request Properties

Example request body

{
  "verification": {
    "status": "inflow_completed",
    "timestamp": "2019-09-03T08:36:25.597Z"
  }
}

RESPONSE

Code : 200 OK

{
  "status": "success",
  "verification": {
    "id": "4bb7b3b1-986c-4134-af72-8683c532cfe2",
    "url": "https://alchemy.veriff.com/v1/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiNGJiN2IzYjEtOTg2Yy00MTM0LWFmNzItODY4M2M1MzJjZmUyIiwiaWF0IjoxNTcxNjU5ODgwfQ.0Z7ZmPM9sNOljt2IfOGisCJGqw4u5AywcpXASmunyo8",
    "vendorData": "11111111",
    "host": "https://alchemy.veriff.com",
    "status": "completed",
    "sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiNGJiN2IzYjEtOTg2Yy00MTM0LWFmNzItODY4M2M1MzJjZmUyIiwiaWF0IjoxNTcxNjU5ODgwfQ.0Z7ZmPM9sNOljt2IfOGisCJGqw4u5AywcpXASmunyo8"
  }
}

Webhooks

To automate the handling of responses from verifications, you will need to implement a webhook listener, according to the documentation at Webhook Decision (POST). Determine the URL of the listener, also, take into account that Veriff doesn't allow custom ports to be added to the webhooks urls (neither on decisions nor on events). Once the URL is determined it can be configured in the Veriff Station under Integrations › Webhook Decision / Events URL. In case you want to test web hooks before the mobile app fully working, it is possible to manually generate verification session in Station and then following the URL for a web-based verification session, which will post the result to your webhook.

Configuring the webhook endpoint

Go to Veriff's back office, Management -> Vendors -> Edit, and set 'Decision notification url' to the URL where your server will be listening for decision notifications from Veriff. Veriff will post decision notifications of verification results to this URL. Only HTTPS URLs are allowed.

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

The full description of the webhook format is at Webhook Decision (POST)

Recognizing your customer

When your server receives a decision notification from Veriff, you have to figure out, which customer is it about.

There are two ways to do this:

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

The other way is to provide Veriff with your internal customer ID, or some other key that uniquely identifies your customer. You can store your identifier in the vendorData element as a string, and we will send it back to you in webhook notifications. 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.

You can secure the webhook listener URL in three ways:

When Veriff calls your webhook endpoint, we use the same logic of X-SIGNATURE generation on our calls to you, as on your calls to us. So, when we call your endpoint for any notification URL, we set the X-AUTH-CLIENT http header to your API key, and we set the X-SIGNATURE header to the hex encoded sha256 digest of the request body and the secret.

When you accept a webhook call from us, you need to proceed as follows:

  1. compare the X-AUTH-CLIENT header to your api key, if different -> fail with error
  2. access the http request body (before it is parsed)
  3. calculate the sha256 digest of the request body string + api secret
  4. compare the hex encoded digest with the X-SIGNATURE header, if different -> fail with error
  5. only now you should parse the request body JSON object

The calculation for X-SIGNATURE is following the same algorithm as for session generation.

Different webhook calls

The difference between the URLs is as follows:

Meaning of the various verification responses

Verification status is one of

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

Explanation of the meaning of the response codes: Webhook response codes

Lifecycle of the verification session

Responses 9001, 9102, 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:

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 verificaton 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: * Age checks * Name validation checks * A user is unable to pass verification as required, but their attempt is sincere and the document is legitimate * A user has made multiple fraudulent attemtps to verify and cannot pass Veriff's verification any more * You wish to restrict the user's access to your platform based on their earlier behavior Veriff provides a more standardized solution where unconventional user behavior is not deferred to for the benefit of overall decision quality.

Meaning of the various event codes

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

The event webhook informs you about the progress of the verification. However, it does not inform you of the decision.

The event code can be one of:

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}

/webhooks /decision (POST)

This endpoint specifies how the asynchronous response to the notification url looks like. It sends the result of the verification back to the vendor once the verification has been reviewed. The notification url needs to be specified in Veriff's backoffice.

Response

Description For testing this endpoint without depending on Veriff sending responses you can use this Curl command. Beware, that the signature will not match your Vendors Key and Secret. To validate this example signature and payload use Secret '3c184872-6929-43d9-91d5-9e68468b5aa1'.

curl --request POST 'https://your.url' -k \
--header 'accept:application/json' \
--header 'x-auth-client:8e4f7cd8-7a19-4d7d-971f-a076407ee03c' \
--header 'x-signature:46e0fb8e7bf550a9f034e0e7a659fbf23085176efdbe76dc247b27c2758952a6' \
--header 'content-type:application/json' \
--data '{"status":"success","verification":{"id":"12df6045-3846-3e45-946a-14fa6136d78b","code":9001,"person":{"gender":null,"idNumber":null,"lastName":"MORGAN","firstName":"SARAH","citizenship":null,"dateOfBirth":"1967-03-30","nationality":null,"yearOfBirth":"1967","pepSanctionMatch":null},"reason":null,"status":"approved","comments":[],"document":{"type":"DRIVERS_LICENSE","number":"MORGA753116SM9IJ","country":"GB","validFrom":null,"validUntil":"2022-04-20"},"reasonCode":null,"vendorData":"12345678","decisionTime":"2019-11-06T07:18:36.916Z","acceptanceTime":"2019-11-06T07:15:27.000Z","additionalVerifiedData":{},"riskLabels":[{"label":"document_crosslinked_with_fraud","category":"document"},{"label":"document_globally_crosslinked_with_multiple_declines","category":"document"}]},"technicalData":{"ip":"186.153.67.122"}}'

Properties

{
  "status": "success",
  "verification": {
    "id": "12df6045-3846-3e45-946a-14fa6136d78b",
    "code": 9001,
    "person": {
      "gender": null,
      "idNumber": null,
      "lastName": "MORGAN",
      "firstName": "SARAH",
      "citizenship": null,
      "dateOfBirth": "1967-03-30",
      "nationality": null,
      "yearOfBirth": "1967",
      "pepSanctionMatch": null
    },
    "reason": null,
    "status": "approved",
    "comments": [],
    "document": {
      "type": "DRIVERS_LICENSE",
      "number": "MORGA753116SM9IJ",
      "country": "GB",
      "validFrom": null,
      "validUntil": "2022-04-20"
    },
    "reasonCode": null,
    "vendorData": "12345678",
    "decisionTime": "2019-11-06T07:18:36.916Z",
    "acceptanceTime": "2019-11-06T07:15:27.000Z",
    "additionalVerifiedData": {
    },
    "riskLabels": [
    {
      "label": "document_crosslinked_with_fraud",
      "category": "document"
    },
    {
      "label": "document_globally_crosslinked_with_multiple_declines",
      "category": "document"

    }]
  },
  "technicalData": {
    "ip": "186.153.67.122"
  }  
}

Properties explained:

/webhooks /events (POST)

Description To keep up to date with the clients progress during the verification process Veriff allows to subscribe to certain events, at the moment three events are triggered, first when client arrives to veriff environment and starts the verification process, second when client is done with the process and submits the attempt,

Properties

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

id: String (required) UUID v4 which identifies the verification session
attemptId: String (required) UUID v4 which identifies session attempt
feature: String (required) Feature on which the event was triggered (selfid refers to the end user flow)
code: (one of 7001, 7002) (required) Event code

For Full list of reason and response codes see documention Response and error codes

7001 Started.
7002 Submitted.

action: Corresponding action description (required)

Response and error 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." }`
Crendentials & 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.`
1811 `Mandatory X-SIGNATURE header containing the SHA256 hash is missing from the request.`
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.`
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.
1501 Vendor data must be a string.
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
101 Physical document not used.
102 Suspected document tampering.
103 Person showing the document does not appear to match document photo.
105 Suspicious behaviour.
106 Known fraud.
107 Velocity/abuse. (Deprecated)
108 Velocity/abuse duplicated user.
109 Velocity/abuse duplicated device.
110 Velocity/abuse duplicated ID.
Resubmit verification.reasonCode
Code Description
201 Video and/or photos missing.
202 Face not clearly visible.
203 Full document not visible.
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"