NAV Navbar
shell

I. API user verification

API Introduction

api_flow

Step 1 : Authentication

Header parameters

// JS example
const authorization = {
  client_id: '5d7a4cb96fcee22ed7a1511f',
  client_secret: 'JZRCJ06KUJKXPCMA59D0ZAUEZ4YB51JX',
};
const authorizationString = `${authorization.client_id}:${authorization.client_secret}`;
const authorizationHeader = `Basic ${Buffer.from(authorizationString).toString('base64')}`;
{
  'Content-Type': 'application/x-www-form-urlencoded',
  Authorization: 'Basic NWQ3YTRjYjk2ZmNlZTIyZWQ3YTE1MTFmOkpaUkNKMDZLVUpLWFBDTUE1OUQwWkFVRVo0WUI1MUpY',
}

Request parameters

/**
 * @param {string} [grant_type=client_credentials] - Authorization grant type
 */

Request sample

curl -X POST \
  https://api.getmati.com/oauth \
  -H 'Authorization: Basic NWQ4NDkxNzNiNmZkMmUwMDFjZWEyYWNjOjlGMVlCMDRBV0pNMVJXQUJCNE1XM0sxWE5QV1kzVjJO' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d grant_type=client_credentials

Response sample

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5",
    "expiresIn": 3600
}

Step 2: Create a new identity

Header parameters

{
  'Content-Type': 'application/json',
  Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cC',
}

Request parameters

/**
 * @param {Object.<string, *>} metadata - Any metadata values to pass through verification
 */

Sample request to create a new mati identity

curl -X POST \
  https://api.getmati.com/v2/identities \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC' \
  -H 'Content-Type: application/json' \
  -d '{"metadata":{"user":"JOHN MALCOVICH","id":"8ad234f293ed89a89d88e12ab"}}'

Sample response

{
    "_id": "5d78fa3a2c6c0449dc4209fa",
    "alive": null,
    "dateCreated": "2019-09-11T13:44:26.096Z",
    "dateUpdated": "2019-09-11T13:44:26.096Z",
    "metadata": {
        "user": "JOHN MALCOVICH",
        "id": "8ad234f293ed89a89d88e12ab"
    },
    "status": "pending",
    "user": "5d3aea392127f21828d6d84b"
}

Request params Description
metadata include JSON metadata to pass whatever information you need. This metadata will be sent inside the verification webhook too. You are then able to know which one of your user got verified.
Response params Description
_id Identity _id
alive Shows does a person alive on liveness or not
metadata Any metadata values to pass through verification
status Identity status
user User _id

Step 3: upload user verification data

Header parameters

{
  Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnQiOnsiX2lkIjoiNWQ4NDkxNzNiNmZkMmUwMDFjZWEyYWNjIiwibWVyY2hhbnQiOiI1ZDg0OTExOGI2ZmQyZTAwMWNlYTJhYzkifSwidXNlciI6eyJfaWQiOiI1ZDg0OTExOGI2ZmQyZTAwMWNlYTJhYzciLCJmaXJzdE5hbWUiOiJKb2huIEFkbWluIiwibGFzdE5hbWUiOiJNYWxrb3ZpYyJ9LCJpYXQiOjE1Njg5NjkzMzcsImV4cCI6MTU2ODk3MjkzNywiaXNzIjoib2F1dGgyLXNlcnZlciJ9.NimhrCKThLwWjQZtfd-RYu97x7Zl89outq6vuPt_qes',
}

Sample BODY for user inputs (to be included into your POST request to Mati)

// JS example 
const inputs = [
  {
    inputType: 'document-photo', 
    group: 0,
    data:
      {
        type: 'national-id',
        country: 'USA', 
        region: 'IL', 
        page: 'front', 
        filename: 'USA-NI-FRONT.jpg', 
      },
  },
  {
    inputType: 'selfie-photo',
    data:
      {
        filename: 'SELFIE_PHOTO.jpg', 
      },
  },
  {
    inputType: 'selfie-video', 
    data:
      {
        filename: 'SELFIE_VIDEO.mp4', 
      },
  },
];

const form = new FormData({});
form.append('inputs', JSON.stringify(inputs));
form.append('document', fs.createReadStream('./USA-NI-FRONT.jpg'), 'USA-NI-FRONT.jpg');
form.append('selfie', fs.createReadStream('./SELFIE_PHOTO.jpg'), 'SELFIE_PHOTO.jpg');
form.append('video', fs.createReadStream('./SELFIE_VIDEO.mp4'), 'SELFIE_VIDEO.mp4');

Response parameters

/**
 * @param {Object[]} response
 * @param {boolean} response.result - Set up to true if input passes an error
 * @param {Object} response.error - Error data
 * @param {string} response.error.code - Error code
*/
Parameter Description
inputType Represents the type of user input. Any of ['document-photo', 'selfie-photo', 'selfie-video']
group Index of verificationSteps with given inputType in merchant configurations
type Any of ["driving-license", "national-id", "passport", "proof-of-residency"]
country Country code. Full list provided on the right section.
region USA states codes. Full list provided on the right section.
page Page id to upload. Any of ['front', 'back']
filename Original file name
Error Description
input.not.found input type is missing in merchant config. To fix check verification requirements on the integration page
input.locked data already uploaded and processing.
media.not.found missing media data. Check that file is assigned to FormData or that "data.filename" param in "inputs" is equal to the real filename
validation.error input data validation fail. Check "data" field params in "inputs".

Sample POST request for: single document photo input

curl -X POST \
  https://api.getmati.com/v2/identities/5d84bebadee2cc001b2167c1/send-input \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsI' \
  -F 'inputs=[{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"US","region":"IL","page":"front","filename":"USA-NI-FRONT.jpg"}}]' \
  -F document=@USA-NI-FRONT.jpg

Sample POST request for: two sided document photos input

curl -X POST \
  https://api.getmati.com/v2/identities/5d84bebadee2cc001b2167c1/send-input \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsI' \
  -F 'inputs=[{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"US","region":"IL","page":"front","filename":"USA-NI-FRONT.jpg"}},{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"US","region":"IL","page":"back","filename":"USA-NI-BACK.jpg"}}]' \
  -F document=@USA-NI-FRONT.jpg \
  -F document=@USA-NI-BACK.jpg

Sample POST request for: selfie photo input

curl -X POST \
  https://api.getmati.com/v2/identities/5d84bebadee2cc001b2167c1/send-input \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsI' \
  -F 'inputs=[{"inputType":"selfie-photo","data":{"filename":"SELFIE_PHOTO.jpg"}}]' \
  -F selfie=@SELFIE_PHOTO.jpg

Sample POST request for: liveness video input

curl -X POST \
  https://api.getmati.com/v2/identities/5d84bebadee2cc001b2167c1/send-input \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsI' \
  -F 'inputs=[{"inputType":"selfie-video","data":{"filename":"SELFIE_VIDEO.mp4"}}]' \
  -F video=@SELFIE_VIDEO.mp4 

Sample POST request for: full user input (documents & biometrics)

curl -X POST \
  https://api.getmati.com/v2/identities/5d84bebadee2cc001b2167c1/send-input \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsI' \
  -F 'inputs=[{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"US","region":"IL","page":"front","filename":"USA-NI-FRONT.jpg"}},{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"US","region":"IL","page":"back","filename":"USA-NI-BACK.jpg"}},{"inputType":"selfie-photo","data":{"filename":"SELFIE_PHOTO.jpg"}},{"inputType":"selfie-video","data":{"filename":"SELFIE_VIDEO.mp4"}}]' \
  -F document=@USA-NI-FRONT.jpg \
  -F document=@USA-NI-BACK.jpg \
  -F selfie=@SELFIE_PHOTO.jpg \
  -F video=@SELFIE_VIDEO.mp4 

Country codes

["AD","AE","AF","AG","AI","AL","AM","AO","AR","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BW","BZ","CA","CD","CF","CG","CH","CI","CL","CM","CN","CO","CR","CW","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","ER","ES","ET","FI","FJ","FR","GA","GB","GD","GE","GH","GM","GN","GQ","GR","GT","GW","GY","HK","HN","HR","HT","HU","ID","IE","IL","IN","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KR","KV","KW","KZ","LA","LB","LC","LI","LK","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MK","ML","MM","MN","MT","MU","MV","MW","MX","MY","MZ","NA","NE","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PG","PH","PK","PL","PS","PT","PY","QA","RO","RS","RU","RW","SA","SB","SC","SE","SG","SI","SK","SL","SM","SN","SO","SR","SS","ST","SV","SZ","TD","TG","TH","TJ","TL","TM","TN","TO","TP","TR","TT","TV","TZ","UA","UG","US","UY","UZ","VA","VE","VN","VU","WS","YE","ZA","ZM"]

USA states

["AL","AK","AZ","AR","CA","CO","CT","DE","DC","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NJ","NM","NY","NC","NH","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]

II. Web SDK user verification

Web SDK code snippet

<script src="https://web-button.mati.io/button.js">
</script>
<mati-button
  clientid="YOUR_CLIENT_ID"
  metadata="JSON_METADATA_STRING"
/>

websdk

III. Mobile SDKs user verification

iOS integration

pod 'Mati-Global-ID-SDK'
pod install

Android integration

implementation 'com.matilock:mati-global-id-sdk:XXX.YYY.ZZZ'

IV. Webhooks - receive user verification data

Introduction

Few important notes:

Webhook Description
verification_completed Webhook sent once Mati is done verifying a user entirely. When you get this webhook, you should GET the "resource" URL to get the verification data about the user.
verification_started Webhook sent at the beginning of the SDK flow, when Mati is making a new verification record (usually at the upload of the first ID document)
verification_expired Webhook sent when verification is not completed after 30 mins. It means that the user probably did not finish the verification flow.
verification_inputs_completed Webhook sent when the user has uploaded all inputs via SDKs. You can use that webhook to know when to redirect the user after the verification flow on your website/App.
verification_updated Webhook sent from your Mati dashboard manually after updating the user verification information.

webhook_flow

Step 1 : Register a webhook URL on your Mati dashboard

If you login to your Mati dashboard, and go the developer section, you will be able to add a new webhook URL. That way Mati knows to what URL it should communicate webhooks related information. You should also add a "webhook secret" (user any string of any length). That assures you that the webhook is indeed coming from Mati.

Step 2: Receive the webhook

Webhook you will receive once user verification is finished

{
  "eventName": "verification_completed",
  "status": "reviewNeeded",
  "metadata": {
    "user_email": "john.smith@gmail.com",
    "user_name": "john_smith",
    "user_id": "123456789"
  },
  "resource": "https://api.getmati.com/v1/verifications/5c6b534bd3efe73404b52341",
  "details": {
    "age": {
      "data": 37
    }
  }
}

You will receive a "verification_completed" webhook. This webhook is sent only once Mati could complete the user verification. This webhook will contain the following fields.

Parameter Description
eventName Webhook type
metadata Metadata that you transmitted to Mati SDKs. You can put any type of metadata here. That way, your backend knows what user just got verified
resource Contains the link you should use in order to get the user verification information

Step 3: Validate webhook signature (optional)

Sample code to validate your webhook signature

const crypto = require('crypto');

// A sample webhook coming from Mati
const WEBHOOK_PAYLOAD = 
{   
    eventName: 'verification_completed',
    metadata: {email: 'john@gmail.com'},
    payload: {},
    resource: 'https://api.mati.io/api/v1/verifications/db8d24783',
};

const MERCHANT_SECRET = 'your_mati_webhook_secret';

// Mati hashes your webhook payload
const signature = crypto.createHmac('sha256', MERCHANT_SECRET).update(JSON.stringify(WEBHOOK_PAYLOAD)).digest('hex');
console.log(signature);

function verify(signature, secret, payloadBody) {
    let hash = crypto.createHmac('sha256', secret);
    hash = hash.update(payloadBody).digest('hex');
    return hash === signature;
}

let isValidPayload;

isValidPayload = verify(signature, MERCHANT_SECRET, JSON.stringify(WEBHOOK_PAYLOAD));
console.log(isValidPayload);

When you create a webhook url on your Mati dashboard, you define a "secret". This "secret" will then be hashed with the "payloadBody" (using SHA256) and sent in the webhook's header. In order to authenticate the webhook received, you should verify that this signature on header 'x-signature' (Signature-Sha-256) is valid. See a code snippet on the right.

Step 4: Get your authentication token

POST request to get a valid token

curl --location --request POST "https://api.getmati.com/oauth/token" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --header "Authorization: Basic $(echo -n client_id:client_secret | base64)" \
  --data "grant_type=client_credentials&scope=identity"

In order to be able to fetch the data inside the resource link, you should first get a valid token. To get a new valid token, it'you just need to perform a POST request and pass as parameters your client_id and client_secret available on your Mati dashboard.

Headers Value
Content-Type application/x-www-form-urlencoded
Authorization Basic
Body Value
grant_type client_credentials
score identity

Step 5: GET users' "resource" URL

GET request on the "resource" URL

curl --location --request GET "<your_resource_url>" \
  --header "Authorization: Bearer {{ACCESS_TOKEN}}"

Response sample

{
        "steps": [
            {
                "status": 200,
                "id": "liveness",
                "data": {
                    "selfieUrl": "http://api.getmati.com/media/selfie/bae11202.jpg",
                }
            }
        ],
        "documents": [
            {
                "type": "passport",
                "photos": [
                    "https://api.getmati.com/v1/media/ae7b963c1f23.jpg",
                    "https://api.getmati.com/v1/media/ae7b963c1f2e.jpg"
                ],
                "steps": [
                    {
                        "status": 200,
                        "id": "alteration-detection",
                    },
                    {
                        "status": 200,
                        "id": "document-reading",
                        "data": {
                            "fullName": {
                                "value": "HUGO JOSE FRANCOU"
                            },
                            "documentNumber": {
                                "value": "AAA722873"
                            },
                            "dateOfBirth": {
                                "value": "1950-07-14T00:00:00.000Z"
                            },
                        },  
                    },
                    {
                        "status": 200,
                        "id": "facematch",
                    },
                    {
                        "status": 200,
                        "id": "watchlists",
                    },
                    {
                        "status": 200,
                        "id": "mexican-curp-validation",
                        "data": {
                            "curp": "GAGF860420HDFRMR08",
                            "fullName": "GARCIA GOMEZ FERNANDO ELIAS",
                            "name": "FERNANDO ELIAS"
                    }
                ],
            },
        ],
        "id": "3452455cb0d48780"
    }

The response also contains your user's device fingerprint

    deviceFingerprint: {
                    ua: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36",
                    browser: {
                      name: "Chrome",
                      version: "76.0.3809.132",
                      major: "76"
                    },
                    engine: {
                      name: "WebKit",
                      version: "537.36"
                    },
                    os: {
                      name: "Windows",
                      version: "10"
                    },
                    cpu: {
                      architecture: "amd64"
                    }
                  }x

If you want to access the user verification data, you need to make a GET request on the "resource" URL contained inside the webhook.

Headers Value
Authorization Basic

The response is organised in a way that it contains verification "steps". These steps are nested. For example step "document-reading" is nested inside step "document"

Steps Type Description
liveness Contains the proof of life response, making sure your user is a real person (and not a printed paper)
document-reading Will return fields read from the document: full name, date of birth, document number, etc...
facematch Face match performed between document face & user's face
watchlists Checks if there is any match found between the name extracted from the document and international watch-lists
alteration-detection Checks that the document was not altered
template-matching Checks that the presented document revision is valid
mexican-curp-validation For certain countries only (only Mexico supported for now)

V. FAQ

How to automate your verification process with Mati?

You are free to build your own logic, but here is the flow most companies are happy with:

1) Let the user go through Mati SDK

2) Receive the frontend callback to know when the user finished the verification flow

3) Display a beautiful waiting page to the user saying "You are under review. Please come back in a few minutes!"

4) Wait for the "verification_completed" webhook, and parse the verification information

5) If you cannot find any errors inside Mati verification steps, automatically let your user access to your service

6) If Mati found some issues with the verification, you should probably manually check the user

What is allowed to pass to metadata?

What should I put for my "webhook secret"?

Feel free to enter any string of any length. As explained inside section II (step2), we use a webhook secret as a way to assure the authenticity of your webhook. Consider this a password that only you should know.

What happens in case a verification step fails?

Invalid verification step sample (alteration-detection)


{
    "error": {
        "code": 400,
        "message": "document is considered tempered"
    },
    "status": 200,
    "id": "alteration-detection",
},

Invalid verification step sample (liveness)



{
    "error": {
        "code": 400,
        "message": "user did not pass liveness"
    },
    "status": 200,
    "id": "liveness",
    "data": {
        "selfieUrl": "http://api.getmati.com/media/selfie/bae11202.jpg",
    }
}

Invalid verification step sample (mexican-curp-validation)



{
    "error": {
        "code": 400,
        "message": "we could not extract anything from CURP database"
    },
    "status": 200,
    "id": "mexican-curp-validation",
    "data": {
    }
},

Whenever Mati detects any risk while verifying a user, some error will be returned in at least 1 verification step. On the right, the example of some error rendered for some verification steps. So when you parse the user verification data, just try to identify any error inside any verification step. If there's none, you can automate that user verification process, and let him fully access your service.

What happens when Mati cannot read a document field?

It happens that documents are too damaged or blurry to be read. In that case Mati will render 'null' as field value to indicate that we could not read this field.

Document reading response when "documentNumber" and "dateOfBirth" could not be read


...

{
    "status": 200,
    "id": "document-reading", 
    "data": {
        "fullName": {
            "value": "HUGO JOSE FRANCOU"
        },
        "documentNumber": {
            "value": null
        },
        "dateOfBirth": {
            "value": null
        },
    },
},

...

How to download selfie & documents images?

GET request to get any image

curl --location --request GET "<your_image_url>" \
  --header "Authorization: Bearer {{ACCESS_TOKEN}}"

Once you got the verification information, you might want to store the images of selfie and documents on your server. For that, you can simply make a GET request.

Headers Value
Authorization Basic

How to know when a user finished Mati Web SDK?

How to subscribe to Mati Web SDK callback


Mati.on("mati:userFinishedSdk", function (data) {
  console.log("<text>", data._args);
});

Mati Web SDK has 2 callbacks, described below.

Callback Description
mati:userFinishedSdk Callback fired when user clicks "done" button at the very end of Mati Web SDK flow. This indicates that the user has finished uploading all necessary information to Mati.
mati:exitedSdk Callback fired when user exits the Mati Web SDK. This indicates that the user has not finished the verification flow.