Introduction
Welcome to the Veriff documentation.
This documentation will help you get started with us. 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.
Once you have set up your password, you will be presented with a QR code to set up the Authenticator application. We use two factor authentication for your Veriff account. You will need to download and install the Google Authenticator app on your mobile device. Please find it in the Apple App Store or in the Google Play Store
Open the app, select the "+" icon and scan the QR code on the screen. Use the generated PIN-code to log in.
New users for your colleagues can be created by an admin 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.
Test Integrations are to be used for development and sessions will not count towards paid usage. Veriff will not provide decisions on sessions created for test integrations.
Sessions created for Live integrations will count towards usage and Veriff will be providing decisions for those.
Sessions Lifecycle
Generating sessions for KYC with Veriff
Generating sessions manually
To generate a verification:
- Click "Verifications" on top menu
- Click "Add Verification" from the opened window
- Choose "Integration"
- Fill in the first name, last name
- Click "Generate Verification"
- Click on or copy the URL and finish the flow
You can see the completed verification in the Verifications list
Generating sessions in code
{
"verification": {
"callback": "https://veriff.me",
"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 KYC link for your user on the web, is to make a simple JSON object containing the user's name and the redirect(callback) URL to which the user will be sent after they complete KYC. Then use HTTP POST to send the object to https://api.veriff.me/v1/sessions, with Content-Type application/json and the X-AUTH-CLIENT header containing your API Key.
In response, we give you a JSON session ID and a URL, as follows:
{
"status": "success",
"verification":{
"id":"f04bdb47-d3be-4b28-b028-............",
"url": "https://magic.veriff.me/v/sample-url.................",
"sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJh............",
"baseUrl": "https://magic.veriff.me"
}
}
The URL of the KYC session is where you should redirect your user in your web front end.
The session ID should be saved on your end - you can tie back the webhook responses to your customer record that way.
Once the end user completes the KYC, they will be redirected back to the callback URL. If you do not specify it for each session call, then there is a default configured in your account settings. This "callback" could be to a "thank you" or waiting page on your side.
To be clear, the callback does not contain any decision or verification information yet. After the KYC process completes in the background, we notify your webhook endpoint (also configured in your account settings), or if you have not automated decisions yet, then you can look up the result by logging in to Veriff Station and checking the Verifications.
The full API for starting a KYC session with Veriff is documented here in the API: #sessions
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:
- in a temporary buffer, concatenate the request body and your API secret. If you stringify the request body then use it at hash calculation as well.
- calculate a SHA 256 hash
- hex encode the hash
For example, if the request object is
{
"verification": {
"callback": "https://veriff.me",
"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.me",
"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.me","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, simply 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.
Integrations
Android SDK integration
Android SDK Requirements
Veriff Android SDK requiers Android 5.0 or newer.
Adding Android SDK
allprojects {
repositories {
maven { url "https://cdn.veriff.me/android/" } //veriff
google()
jcenter()
}
}
Open the root build.gradle
file and add a new maven destination to the repositories in the allprojects section.
Add the Veriff SDK dependency to the application build.gradle
file:
implementation 'com.veriff:veriff-library:2.6.2'
Permissions
The SDK will request all the permissions it needs, please make sure that the CAMERA, RECORD_AUDIO, and WRITE_EXTERNAL_STORAGE 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.
Veriff.Builder veriffSDK = new Veriff.Builder("https://magic.veriff.me", sessionToken);
veriffSDK.launch(MainActivity.this, REQUEST_CODE);
Parameters are defined as below
Parameters | Description |
---|---|
sessionToken | sessionToken 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 the vendor activity which can be used to compare in onActivityResult |
Customizing the SDK (Optional)
Starting from version 2.6.0
you can customize the look and feel of the SDK flow by setting a Branding
object to a Veriff.Builder
object before launching as shown below.
final Branding branding = new Branding.Builder()
.setThemeColor(getResources().getColor(R.color.theme_color))
.setToolbarIcon(R.drawable.toolbar_icon)
.setNotificationIcon(R.drawable.notification_icon)
.build();
veriffSDK.setBranding(branding);
veriffSDK.launch(MainActivity.this, 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.
Getting the verification status
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && data != null) {
int statusCode = data.getIntExtra(VeriffConstants.INTENT_EXTRA_STATUS, Integer.MIN_VALUE);
String sessionToken = data.getStringExtra(VeriffConstants.INTENT_EXTRA_SESSION_URL);
handleResult(statusCode, sessionToken); //see below to know how to handle result
}
super.onActivityResult(requestCode, resultCode, data);
}
public void handleResult(int statusCode, String sessionToken){
if (statusCode == VeriffConstants.STATUS_USER_FINISHED) {
// User finished the session, there might be other callbacks coming after this one. (for example if the images are still being uploaded in the background)
} else if (statusCode == VeriffConstants.STATUS_ERROR_NO_IDENTIFICATION_METHODS_AVAILABLE) {
// No identifications methods available
} else if (statusCode == VeriffConstants.STATUS_ERROR_SETUP) {
// issue with the provided vendor data
} else if (statusCode == VeriffConstants.STATUS_ERROR_UNKNOWN) {
// Unknown error occurred.
} else if (statusCode == VeriffConstants.STATUS_ERROR_NETWORK) {
// Network is unavailable.
} else if (statusCode == VeriffConstants.STATUS_USER_CANCELED) {
// User cancelled the session.
} else if (statusCode == VeriffConstants.STATUS_UNABLE_TO_ACCESS_CAMERA) {
// Failed to access device's camera. (either access denied or there are no usable cameras)
} else if (statusCode == VeriffConstants.STATUS_UNABLE_TO_START_CAMERA) {
// Failed to start the device's camera.
} else if (statusCode == VeriffConstants.STATUS_UNABLE_TO_RECORD_AUDIO) {
// Failed to access device's microphone.
} else if (statusCode == VeriffConstants.STATUS_SUBMITTED) {
// Photos are successfully uploaded.
} else if (statusCode == VeriffConstants.STATUS_ERROR_SESSION) {
// Invalid sessionToken is passed to the Veriff SDK.
} else if (statusCode == VeriffConstants.STATUS_DONE) {
// Session is completed from clients perspective.
} else if (statusCode == VeriffConstants.STATUS_ERROR_UNSUPPORTED_SDK_VERSION) {
// This version of Veriff SDK is deprecated.
}
}
Veriff SDK sends callbacks to vendor`s mobile application. To capture the result override the onActivityResult
method in the vendor activity that started the verification flow:
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
Veriff.setLoggingImplementation(<Instance of your logging class>);
Veriff.Builder veriffSDK = new Veriff.Builder(baseUrl, sessionToken);
veriffSDK.launch(MainActivity.this, REQUEST_CODE);
To turn on logging, you simply add your logging implementation instance (instance of LogAccess class) to the SDK before launching the SDK as shown.
Android SDK Changelog
Below is a summary of changes introduced with each version of the Android SDK
Versions
- 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
- Fixed a crash when changing language on Android 6.x or older
- 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
- 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
- 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
- Added support for Mexican Spanish
- Removed white screen as flash while taking selfie
- WebRTC updates and improvements
- 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
- Fixed dark room warning not being translated
- Added Japanese language
- Added dark room detection
- Fixed a few issues around ending the video session
- 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
- Fixed an issue with capturing photos on Android 5.x devices
- 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
- Increased minSdkVersion to 21
- Added video mandatory flag
- Increased socket write timeout when uploading
- Added Arabic support
- Other camera/webrtc fixes and improvements
- Added new languages: Georgian, Hindi, Malay, Ukranian
- Fixed multiple copy issues
- 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
- 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
- Critical hot fix for 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
- 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
- Added support for preselected country and document (Fixes #3)
- 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
- WebRTC beta support with a fallback
- Event tracking for business funnel
- Implemented crash reporting
- Improved performance while taking snapshots
- New design
- Integration changes
- Reduced library size
- Optimizations
- Fixed camera issue with older Android versions
- Fixed English langauge resource
- Added gradle publish script
- Added Italian, Dutch and French languages
- Removed Firebase dependency requirement
- Removed Twilio dependency requirement
- Removed Eventbus dependecy requirement
- Added missing translations
- Fixed indefinite upload bug
- Updated language resources
- New versioning management
- Upgraded gradle version
- Upgraded dependecy versions
- Fixed language persistence issue
- Further improved Latvian language resources
- New language selection
- New color schema
- Improved face detection
- Improved Latvian language resources
- Refactoring and general bug fixes
- New library wide toolbar with cancelation and language selection options
- Changed text values and removed unused resources
- Design improvements
- Refactoring and general bug fixes
- 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
Release 2.6.2
Release 2.6.1
Release 2.6.0
Release 2.5.0
Release 2.4.8
Release 2.4.7
Release 2.4.6
Release 2.4.5
Release 2.4.4
Release 2.4.3
Release 2.4.2
Release 2.4.1
Release 2.4.0
Release 2.3.6
Release 2.3.5
Release 2.3.4
Release 2.3.2
Release 2.3.1
Release 2.3.0
Release 2.2.1
Release 2.1.1
Release 2.1.0.
Release 2.0.0.
Release 1.6.8.
Release 1.6.7.
Release 1.6.6.
Release 1.6.5.
Release 1.6.4.
Release 1.6.3.
Release 1.6.2.
Release 1.6.1.
Release 1.6.0.
Release 1.5.0.
Release 1.4.0.
iOS SDK integration
iOS SDK Requirements
Integration Veriff iOS SDK requires at least iOS version 10.0
Add framework to a project
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
github "socketio/socket.io-client-swift" ~> 15.0.0
github "daltoniam/Starscream" ~> 3.1.0
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.
NSCameraUsageDescription
NSMicrophoneUsageDescription
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]
let conf = VeriffConfiguration(sessionToken: token, sessionUrl: "https://magic.veriff.me")
let 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
extension VerificationService: VeriffDelegate {
func onSession(result: VeriffResult, sessionToken: String) {
switch result.code {
case .STATUS_DONE:
// code
case .STATUS_ERROR_SESSION:
// code
...
...
}
}
}
Veriff SDK returns a number of result codes, that application can handle. Implement VeriffDelegate
and assign it to delegate property of Veriff
instance.
Here is list of all possible status codes
UNABLE_TO_ACCESS_CAMERA
User denied access to the cameraSTATUS_USER_CANCELED
User canceled the verification processSTATUS_SUBMITTED
User submitted the photosSTATUS_ERROR_SESSION
The session token is either corrupt, or has expired. A new sessionToken needs to be generatedSTATUS_ERROR_NETWORK
SDK could not connect to backend servers.STATUS_ERROR_NO_IDENTIFICATION_METHODS_AVAILABLE
Given session cannot be started as there are no identification methodsSTATUS_DONE
The session status is finished from clients perspectiveSTATUS_ERROR_UNKNOWN
An unknown error occurred
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 UIWindow
s' 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
- 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
- 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.
- Xcode 11.2.1 & Swift 5.1.2 support.
- WebRTC improvements for iOS 13.
- Added US driver's license barcode scanning.
- Added support for 11 new languages.
- Added support for Mexican Spanish.
- Removed white screen as flash while taking selfie.
- Session validity token implemented.
- Privacy policy web pages localized.
- Performance & Analytics improvements.
- Support for changing UI colors.
- Japanese language support.
- Performance & Analytics improvements.
- UI improvements.
- Turkish language support.
- Performance & Analytics improvements.
- Camera freezeing when coming from background fixed.
- Enhanced logging output.
- Video mandatory added.
- RTL support added.
- Use correct strings for instruction view subtitles
- Remove 3rd party code from repo
- Tap to focus camera
- Camera started log event added
- Preselected document string change
- User can change country and document
- Country list is now translated
- Updated translations
- Device info logging
- Added support for preselected country and document (Fixes #2)
- New languages added: Czech and Lithuanian
- Bitcode support
- WebRTC video(bitcode support will follow in 2.2.1)
- WebRTC beta support(face detection and bitcode support will follow in 2.2.1)
- Xcode 10.2 support
- Improving uploading conversions for devices on slow networks
- Fallback to device language if session is missing language
- Minor UI changes
- Restores Xcode debugging support
- Bugfixes
- Navigation bar appearance issues fixed
- Restored support for i386 x86_64 architectures
- New design
- Added country selection
- Integration changes
- Reduced library size
- Optimizations
- New languages added: Dutch, French, Italian
- Removes video call functionality
- New languages added: Chinese, Polish, Portuguese, Spanish, Vietnamese
- Translations updated for existing languages
- Fixes storage wipe issue on cancel verification
- Updates Latvian and Russian translations
- Fixes issue with method name collision
- Minor design changes
- Fixes crash on upload
- New language switcher design
- Updates to default colors in UI
- Latvian translations updated
- General bugfixes
- 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
- Improved camera stability
- Improved logging
- Updated Latvian translations
- General bug fixes
2.7.0
2.6.2
2.6.1
2.6.0
2.5.2
2.5.1
2.5.0
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.0
2.2.0
2.1.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.2
1.5.1
1.5.0
1.4.0
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/veriff.min.js'></script>
add the CSS styles:
<link rel='stylesheet' href='https://cdn.veriff.me/sdk/js/styles.css'>
or install it via a package manager, styles are added inline
$ 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>
In order to initialize the library, API Key, parentId and onSession callback function is required.
var 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:
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"
}
}
In case the Given name / Last name or both are known, they can be passed to the SDK, therefore text input fields will not be rendered.
veriff.setParams({
person: {
givenName: 'Foo',
lastName: 'Bar'
}
});
// additionally the input labels and button text value can be customised.
veriff.mount({
formLabel: {
givenName: 'First name',
lastName: 'Family name'
},
submitBtnText: 'Veriff Me'
loadingText: 'Please wait...'
});
InContext Javascript SDK Integration
This is an experimental integration and API might change.
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
createVeriffFrame({
url,
onEvent: function(event) {
//
}
})
event
:CANCELED
- user closed the modal.FINISHED
- user finished verification flow.
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 });
Modal details
createVeriffFrame([options])
Create modal with Veriff application.
[options]
- Configuration object.url
- Veriff session URLonEvent
- Event callback function
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.
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:
- you wish to completely implement your own front-end
- you wish to do an offline bulk audit of previously verified customers
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.
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
- New session is link generated
- Session data is saved with your customer record
- Sessions with odd names can be started
- Sessions with vendorData - do you get them back and do you perform actions on them
- User is graned access to your platform after receiving an "Approved" decision
- User is notified about verification failure after receiving "Resubmission" or "Declined" decision
- User is prompted to try again after receiving "Resubmission" decision
- In case of resubmitted session, user is directed to same SessionURL
- In case of disrupted session (browser close, user logout, etc), user should be directed back to earlier session
- In case generated session is over 7 days old (and thus in Expired or Abandonded status)- new session is generated
- At end of verification, callback URL redirects back to the correct place in your platform
Testing security
- Decision webhook with wrong API key should not be accepted
- Decision webhook with mismatched X-SIGNATURE should not be accepted
- Decision with invalid JSON should not break or crash your server
Testing responses
Each type of response should be accepted:
- Approved (Decision endpoint)
- Declined (Decision endpoint)
- Resubmission Requested (Decision endpoint)
- Expired (Event endpoint)
- Abandoned (Event endpoint)
Testing process in your app
You should test the handling of best cases as well as edge cases
- approved users handled or notified as appropriate
- declined users handled as appropriate, your back office notified if necessary
- in case of resubmission request, user is invited back to try KYC again using the same sessionURL
- in case of Expired or Abandoned session (after 7 days), user is not offered to continue from same session, new session URL is created
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/)
- Download and install Node.js
- Download and extract js-integration-demo-master.zip
- Open Command (Windows) or Terminal (Mac) on your local computer.
- Navigate to js-integration-demo-master folder
cd C:\Users\User\Desktop\Veriff API).
- Run command >npm install
- 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.
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://api.veriff.me/v1
Note: Headers are sent with each request
Lifecycle of a verification:
Endpoints
In this post request the vendor sends the data of the client to Veriff and receives in return a unique sessionToken that is related to the client. You can find an example implementation for Javascript. https://github.com/Veriff/js-integration-demo
Available endpoints and methods:
/sessions
Request Properties explained:
callback
:String
| default is defined on settings - Callback url to which the client is redirected after the verification session is completedperson
:object
- Person to be verified
firstName
:String
- First namelastName
:String
- Last nameidNumber
:String
- Identification numbergender
:String
- Gender
document
:object
- Document of a person to be verified
number
:String
- Document numbercountry
:ISO-2
- String Issuing country of the documenttype
:PASSPORT
- (PASSPORT, ID_CARD, DRIVERS_LICENSE, RESIDENCE_PERMIT) Document type
additionalData
:object
- Data from the filled application. JSON object should not have any nested objects it’s just simple key value pairvendorData
:string
- Vendor specific data string, max 40 characters long, will be sent back unmodified using webhookslang
:String
- | default: en User preferred language
timestamp: String (required)
Combined ISO 8601 date and time in UTC (YYYY-MM-DDTHH:MM:S+Timezone Offset|Z, i.e., 2018-04-18T11:02:05.261Z)
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 \
https://api.veriff.me/v1/sessions/ \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: Your-API-KEY' \
-d '{
"verification": {
"callback": "https://veriff.me",
"person": {
"firstName": "Tundmatu",
"lastName": "Toomas",
"idNumber": "123456789"
},
"document": {
"number": "B01234567",
"type": "PASSPORT",
"country": "EE"
},
"additionalData": {
"citizenship": "EE",
"placeOfResidence": "Tallinn"
},
"vendorData": "11111111",
"lang": "en",
"timestamp": "2016-05-19T08:30:25.597Z"
}
}'
Node.js
var request = require("request");
var options = { method: 'POST',
url: 'https://api.veriff.me/v1/sessions/',
headers:
{ 'Content-Type': 'application/json',
'X-AUTH-CLIENT': 'Your-api-key' },
body:
{ verification:
{ callback: 'https://veriff.me',
person:
{ firstName: 'Tundmatu',
lastName: 'Toomas',
idNumber: '123456789' },
document: { number: 'B01234567', type: 'PASSPORT', country: 'EE' },
additionalData: { citizenship: 'EE', placeOfResidence: 'Tallinn' },
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 = 'https://api.veriff.me/v1/sessions/'
payload = json.dumps({
'verification': {
'callback': 'https://veriff.me',
'person': {
'firstName': 'Tundmatu',
'lastName': 'Toomas',
'idNumber': '123456789'
},
'document': {
'number': 'B01234567',
'type': 'PASSPORT',
'country': 'EE'
},
'additionalData': {
'citizenship': 'EE',
'placeOfResidence': 'Tallinn'
},
'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:
status
:String
(required) Request statusverification
:object
(required) Verification objectid
:UUID
-v4 String (required) UUID v4 which identifies the verification sessionurl
:String
(required) URL of the verification to which the person is redirected (Combination of the baseUrl and sessionToken)sessionToken
:String
(required) Session specific token of the verificationbaseUrl
:String
(required) The base url the sessionToken can be used for
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://magic.veriff.me/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214",
"sessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3Rva2VuIjoiOThiYzdjMjEtZTQ0Yy00MTZiLTkxOTMtMTU5ZGZkMzBmMDg4Iiwic2Vzc2lvbl9pZCI6Ijc2ODhmMzYzLTAyZjctNDE1My1iMzM1LWE0ODQ3OTRkMzZmNyIsImlhdCI6MTUwMTIyODI1MSwiZXhwIjoxNTAxODMzMDUxfQ.bMEF37E6-zT2Aa6Q8UXK3B_ZL51w6D_lxnGgQvhj214",
"baseUrl": "https://magic.veriff.me"
}
}
/sessions/{sessionId}
Method to change the status of the verification.
Request properties explained:
verification
:object
(required)frontState
:String
(required) Type of a verification state to switch on to (done)status
:String
(required) Status of a verification (submitted)timestamp
:String
(required) Combined ISO 8601 date and time in UTC YYYY-MM-DDTHH:MM:S+Timezone Offset|Z, i.e., 2018-04-18T11:02:05.261Z)
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 \
https://api.veriff.me/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",
"frontState": "done",
"timestamp": "2019-10-29T06:30:25.597Z"
}
}'
Node.js
var request = require("request");
var options = { method: 'PATCH',
url: 'https://api.veriff.me/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',
frontState: 'done',
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 = 'https://api.veriff.me/v1/sessions/{sessionId}'
payload = json.dumps({
'verification': {
'status': 'submitted',
'frontState': 'done',
'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:
status
:String
(required) Request statusverification
:object
(required) Verification objectid
:UUID-v4
String (required) UUID v4 which identifies the imageurl
:String
(required) URL for the timestamphost
:String
(required) Host URIstatus
:String
(required) Status of the verificationsessionToken
:String
(required) Session specific token of the verification
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://front.veriff.me/v/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiZmQ1YzE1NjMtMWQyMy00YjFhLWFlNDYtN2JhNDI5OTI3ZWQ4IiwiaWF0IjoxNTcyMzM4MDIwfQ.3HbNq0YWKAfFrH-P658_WXMwcUMubyC1aXAMo-umfCU",
"vendorData": "11111111",
"host": "https://front.veriff.me",
"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 \
https://api.veriff.me/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: 'https://api.veriff.me/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 = 'https://api.veriff.me/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:
status
:String
(required) Status of the requestvideos
:object
(required) Array of JSON objects identifying the videos
id
:UUID
-v4 (required) Video Idname
:String
(required) Video nameduration
:String
(required) Video duration in secondsurl
:String
(required) Video download urlsize
:String
(required) Video size in KBtimestamp
:object
(required) Timestamp objectid
:UUID-v4
(required) Timestamp Idurl
:String
(required) Timestamp url
images
:object
(required) Array of JSON objects identifying the imagesid
:UUID
-v4 (required) Image Idname
:String
(required) Image nameurl
:String
(required) Image download urlsize
:String
(required) Image size in KBtimestamp
:object
(required) Timestamp objectid
:UUID-v4
(required) Timestamp Idurl
:String
(required) Timestamp url
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": [
{
"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}"
}
]
}
/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:
image
:object
(required)context
:String
(required) Type of a document (face|document-front|document-back)content
:String
base64 encoded image (png|jpg|jpeg)- Example
data:image/png;base64,R0lGODlhAQABAAAAACw=
timestamp
:string
(required) Combined ISO 8601 date and time in UTC (YYYY-MM-DDTHH:MM:S+Timezone Offset|Z, i.e., 2018-04-18T11:02:05.261Z)
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 \
https://api.veriff.me/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: 'https://api.veriff.me/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.js
import requests
import json
import pprint
url='https://api.veriff.me/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('GET', url, data=payload, headers=headers)
pprint.pprint(response.json())
Response:
Response properties explained:
status
:String
(required) Request statusimage
:object
(required) Image objectid
:UUID-v4
String (required) UUID v4 which identifies the imagename
:String
(required) File name that identifies the imagetimestamp
:String
(required) Timestamp objectsize
:Integer
(required) Size for the imagemimetype
:String
(required) Type for the imageurl
:String
(required) URL for the image
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":{
"id": "39388f8d-c6d6-4e9b-92c6-6978b2e8d664",
"name": "document-back.jpg",
"timestamp":{
"url": "https://magic.veriff.me/v1/timestamps/6f7e2967-4953-4e35-9aed-4ba69f65db80",
"id": "6f7e2967-4953-4e35-9aed-4ba69f65db80" },
"size": 52268,
"mimetype": "image/png",
"url": "https://magic.veriff.me/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 \
https://api.veriff.me/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: 'https://api.veriff.me/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 = https://api.veriff.me/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}/timestamps
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/timestamps \
-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/timestamps',
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}/timestamps'
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
Properties explained:
status
:String
(required) Request statustimestamps
:object
(required) Timestamp objectid
:UUID-v4
(required) Timestamp Idurl
:String
(required) Timestamp urlcontext
:String
(required) Timestamp context
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 RESPONE
{
"status": "success",
"timestamps": [
{
"id": "{TIMESTAMP_ID}",
"url": "{GUARDTIME_TIMESTAMP_URL}",
"context": "{TIMESTAMP_TYPE}"
}
]
}
/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 \
https://api.veriff.me/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: 'https://api.veriff.me/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 = 'https://api.veriff.me/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:
status
:String
(required) Status of the requestverifications
:object
(required) Array of JSON objects identifying the attemptsid
:UUID
-v4` (required) attempt Idstatus
:String
(required) status of the attempt
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"
}
]
}
/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:
status
:String
(required) Status of the responseverification
:object
Verification request decision object. Null if decision is not available yet
id
:String
UUID v4 which identifies the verification sessionstatus
:approved
(one of approved, resubmission_requested, declined, expired, abandoned) Verification statuscode
:9001
(one of 9001, 9102, 9103, 9104, 9151) Verification response code.
Explanation of the meaning of the response codes
- 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 get's 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".
- 9151 : Intermediate Positive: SelfID was successful - this code is only send if the configuration flag is set.
- 9161 : Intermediate Positive: Video Call was successful - this code is only send if the configuration flag is set.
reason
:String
Reason of failed Verificationperson
:object
Verified personfirstName
:String
First namelastName
:String
Last nameidNumber
:String
Identification numbercitizenship
:ISO-2
String CitizenshipdateOfBirth
:String
(YYYY-MM-DD) Date of birthgender
:String
(M, F or Null) Gender
document
:object
Verified documentnumber
:String
Document numbertype
:String
(one of PASSPORT, ID_CARD, DRIVERS_LICENCE, RESIDENCE_PERMIT) Document typecountry
:
ISO-2` String Document countryvalidFrom
:String
Document is valid from datevalidUntil
:String
Document is valid undit date
additionalVerifiedData
:object
Additional vendor specific data which were verified by the verification specialistcomments
:object
Array of additional comments by verification specialisttype
:String
The type of the commentcomment
:String
The comment itselftimestamp
:String
UTC timestamp
technicalData
:object
Technical data objectip
:String
Ip of the device from which the verification was made
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
/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 \
https://api.veriff.me/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: 'https://api.veriff.me/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.js
import requests
import json
import pprint
API_SECRET = 'Your-api-secret'
ATTEMPT_ID= 'attemptId'
url = 'https://api.veriff.me/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:
status
:String
(required) Status of the requestvideos
:object
(required) Array of JSON objects identifying the videosid
:UUID
-v4 (required) Video Idname
:String
(required) Video nameduration
:String
(required) Video duration in secondsurl
:String
(required) Video download urlsize
:String
(required) Video size in KBtimestamp
:object
(required) Timestamp objectid
:UUID-v4
(required) Timestamp Idurl
:String
(required) Timestamp url
images
:object
(required) Array of JSON objects identifying the imagesid
:UUID-v4
(required) Image Idname
:String
(required) Image nameurl
:String
(required) Image download urlsize
:String
(required) Image size in KBtimestamp
:object
(required) Timestamp objectid
:UUID-v4
(required) Timestamp Idurl
:String
(required) Timestamp url
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": [
{
"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/{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 \
https://api.veriff.me/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: 'https://api.veriff.me/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 = 'https://api.veriff.me/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.text)
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
/timestamps/{timestampId}
Get the timestamp with timestampId = {timestampId}
Request method: GET
Media type: application/json
Type: object.
Headers
X-AUTH-CLIENT: string (required)
- API Key
X-SIGNATURE: string (required)
- Timestamp ID signed with API Secret.
Content-Type: application/json
curl
curl -X GET \
https://api.veriff.me/v1/timestamps/fe7fab32-cb78-4b8d-bd65-33e7da6ac2c9 \
-H 'Content-Type: application/json' \
-H 'X-AUTH-CLIENT: Your-api-key' \
-H 'X-SIGNATURE: ea9623fc8dc546b134d504fa4d7d33328b6b4bdea9e7fa6fcd81ac36c8a59954' \
Node.js
var request = require("request");
var options = { method: 'GET',
url: 'https://api.veriff.me/v1/timestamps/fe7fab32-cb78-4b8d-bd65-33e7da6ac2c9',
headers:
{ 'Content-Type': 'application/json',
'X-SIGNATURE': 'ea9623fc8dc546b134d504fa4d7d33328b6b4bdea9e7fa6fcd81ac36c8a59954',
'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'
TIMESTAMP_ID='timestampId'
url = 'https://api.veriff.me/v1/timestamps/{timestampId}'
headers = {
'X-AUTH-CLIENT': 'Your-api-key',
'X-SIGNATURE': generateSignature(TIMESTAMP_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",
"timestamp": {
"id": "{TIMESTAMP_ID}",
"context": "{TIMESTAMP_CONTEXT}",
"signature": "{SIGNATURE}",
"dataHash": {
"algorithm": "SHA-256",
"value": "{HASH_VALUE}"
}
}
}
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.me",
"person": {
"firstName": "Tundmatu",
"lastName": "Toomas",
"idNumber": "1234567890"
},
"document": {
"number": "B01234567",
"type": "PASSPORT",
"country": "EE"
},
"additionalData": {
"citizenship": "EE",
"placeOfResidence": "Tallinn"
},
"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.me",
"person": {
"firstName": "Tundmatu",
"lastName": "Toomas",
"idNumber": "1234567890"
},
"document": {
"number": "B01234567",
"type": "PASSPORT",
"country": "EE"
},
"additionalData": {
"citizenship": "EE",
"placeOfResidence": "Tallinn"
},
"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: dc813ed3551b64e2539a4e8c8b06945dcf82d2656a9700101ce4864fb81410fa
X-Signature included to your request headers is above hash:
Headers
Content-Type: application/json
X-AUTH-CLIENT: Your-API-Key
X-SIGNATURE: dc813ed3551b64e2539a4e8c8b06945dcf82d2656a9700101ce4864fb81410fa
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 'http://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":"Toomas","firstName":"Tundmatu","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: 253e28b027ca93d323d524d437b13ccc06730bd08709aac0194725b63e50b556
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://magic.veriff.me/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: 253e28b027ca93d323d524d437b13ccc06730bd08709aac0194725b63e50b556
SHA-256 hash: 253e28b027ca93d323d524d437b13ccc06730bd08709aac0194725b63e50b556
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}"
}
]
}
- status: String (required) Status of the request
- videos: object (required) Array of JSON objects identifying the videos
- id: UUID-v4 (required) Video Id
- name: String (required) Video name
- duration: String (required) Video duration in seconds
- url: String (required) Video download url
- size: String (required) Video size in KB
- timestamp: object (required) Timestamp object
- id: UUID-v4 (required) Timestamp Id
- url: String (required) Timestamp url
- images: object (required) Array of JSON objects identifying the images
- id: UUID-v4 (required) Image Id
- name: String (required) Image name
- url: String (required) Image download url
- size: String (required) Image size in KB
- timestamp: object (required) Timestamp object
- id: UUID-v4 (required) Timestamp Id
- url: String (required) Timestamp url
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
Timestamps
/timestamps /{timestampId}
Get the timestamp with timestampId = {timestampId}
Request
URI Parameters timestampId: string (required)
Response
{
"status": "success",
"timestamp": {
"id": "{TIMESTAMP_ID}",
"context": "{TIMESTAMP_CONTEXT}",
"signature": "{SIGNATURE}",
"dataHash": {
"algorithm": "SHA-256",
"value": "{HASH_VALUE}"
}
}
}
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": "Not Found." }` |
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. |
1101 | Invalid Vendor API Key. |
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. |
1301 | Requested features are not supported. |
1302 | Only HTTPS return URLs are allowed. |
1500 | Vendor data cannot be more than 40 symbols. |
1501 | Vendor data must be a string. |
2003 | Date of birth is not a valid date. |
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 | |
Status | /descision Webhook and Endpoint verification.code object |
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 get's 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" |
9151 | Intermediate Positive: SelfID was successful - this code is only send if the configuration flag is set. |
Decline | /descision webhook and endpoint 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. |
Resubmit | /descision webhook and endpoint 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. |
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) Once you have determined the URL of the listener, it is possible to configure it in the Veriff back office (Integrations / Notifications / Decision webhook url ) In case you want to test web hooks before the mobile app fully working, it is possible to hand generate verification sessions in the back office (Verifications / Generated tokens / Generate session 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 multiple times 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:
- using the Veriff session ID, or
- using your own customer ID.
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 the administrative interface at Veriff Back Office
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:
- 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.
Different webhook calls
The difference between the URLs is as follows:
- the Decision Webhook Url is where we post decisions about verifications (approved, declined, resubmission_required, expired, abandoned, etc)
- the Event Webhook Url is where we post events during the lifecycle of a verification (started, submitted, etc)
- finally, there is a Callback URL, which is actually the redirect URL for the user on the web site at the end of KYC
Meaning of the various verification responses
Verification status is one of
- approved
- resubmission_requested
- declined
- expired
- abandoned
Verification response code is one of 9001, 9102, 9103, 9104, 9151, 9161 All codes and explanations can be found from Response and error codes
Explanation of the meaning of the response codes: Webhook response codes
- 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 get's 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"
- 9151 : Intermediate Positive: SelfID was successful - this code is only send if the configuration flag is set
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.
Responses 9151 and 9161 are intermediate responses. The session is being processed, it is not open for the end user, and you will soon receive one of the above conclusive or inconclusive responses.
Preconditions for approval decisions
We give a positive conclusive decision (status approved, code 9001) when the user has provided us with:
- photos and video uploaded to us
- the data on the document is readable and matches throughout the document
- the user's portrait photo is recognizable
- the user on the portrait photo corresponds to the person on the document photo
- the document is valid (dates, etc) A positive decision means that the person was verified. The verification process is complete. Accessing the KYC session URL again will tell * the user that nothing more is to be done.
Reasons for negative conclusive decisions
- Physical document not used
- Suspected document tampering
- Person showing the document does not appear to match document photo
- Suspicious behaviour
- Velocity/abuse
- Known fraud
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
- Video and/or photos missing
- Face not clearly visible
- Full document not visible
- Poor image quality
- Document damaged
- Document type not supported
- Document expired
*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 verificatoin 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:
- '7001 : Started
- '7002 : Submitted
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 comlpeted 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:cdffd99617c46e20d37d1db6e4f1bfefb929409e36a59122b6de25c6924d3c65' \
--header 'content-type:application/json' \
--data '{"status":"success","verification":{"id":"a4c0a230-859a-4861-b99b-ac39e4a05bfd","status":"resubmission_requested","code":9103,"reason":"Photos are not from the same document.","acceptanceTime":"2017-09-26T12:27:55.000Z","decisionTime":"2017-09-26T12:34:21.000Z","person":{"firstName":"Morton","lastName":"Stracke","idNumber":"48002020011"},"document":{"number":"AA0052283","type":"ID_CARD","validFrom": "2017-02-27", "validUntil": "2022-02-27"},"comments":[{"type": "General Comment", "comment": "Person refuses to take off hat.", "timestamp": "2017-10-10T13:18:44.009Z"}],"technicalData":{"ip":"124.153.46.66"}}}'
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": null,
"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": {
}
},
"technicalData": {
"ip": "186.153.67.122"
}
}
Properties explained:
status
:String
(required) Status of the responseverification
:object
Verification request decision object. Null if decision is not available yet
id
:String
UUID v4 which identifies the verification sessionstatus
:approved
(one of approved, resubmission_requested, declined, expired, abandoned) Verification statuscode
:9001
(one of 9001, 9102, 9103, 9104, 9151) Verification response code. See Response and error codesreason
:String
Reason of failed Verificationperson
:object
Verified personfirstName
:String
First namelastName
:String
Last nameidNumber
:String
Identification numbercitizenship
:ISO-2
String CitizenshipdateOfBirth
:String
(YYYY-MM-DD) Date of birthnationality
:String
NationalitypepSanctionMatch
:String
PEP check resultgender
:String
(M, F or Null) Gender
document
:object
Verified documentnumber
:String
Document numbertype
:String
(one of PASSPORT, ID_CARD, DRIVERS_LICENCE, RESIDENCE_PERMIT) Document typecountry
:
ISO-2` String Document countryvalidFrom
:String
Document is valid from datevalidUntil
:String
Document is valid undit date
comments
:object
Array of additional comments by verification specialisttype
:String
The type of the commentcomment
:String
The comment itselftimestamp
:String
UTC timestamp
additionalVerifiedData
:object
Additional vendor specific data which were verified by the verification specialistvendorData
:object
Additional vendor specific data.reasonCode
:101
Physical document not used. See Response and error codesdecisionTime
:string
TimestampacceptanceTime
:string
Timestamp
technicalData
:object
Technical data objectip
:String
Ip of the device from which the verification was made
/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",
"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) 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": "Not Found." }` |
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. |
1101 | Invalid Vendor API Key. |
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. |
1301 | Requested features are not supported. |
1302 | Only HTTPS return URLs are allowed. |
1500 | Vendor data cannot be more than 40 symbols. |
1501 | Vendor data must be a string. |
2003 | Date of birth is not a valid date. |
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 | |
Status | /descision Webhook and Endpoint verification.code object |
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 get's 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" |
9151 | Intermediate Positive: SelfID was successful - this code is only send if the configuration flag is set. |
Decline | /descision webhook and endpoint 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. |
Resubmit | /descision webhook and endpoint 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. |