NAV
Shell

Mati Verification Process

Introduction

The Mati allows you to programmatically verify your users, in an easy and fully customizable way. Mati is built to make your company KYC/AML compliant! For more information visit getmati.com

We offer two ways to execute our verification flow:

We strongly recommend to use our SDKs to verify your users, since Mati already puts a lot of energy into supporting most devices, old and new smartphones, giving out the best UI/UX. But if for some reason you can't use our SDKs, we have an official API with which can send us your users' data that we will verify.

api_flow

You can also go to our flowchart so you better understand how we verify users and what can you expect from each check.

Mati Dashboard

You can customize your verification flow to best satisfy your business needs and your user base. It does not matter where they might be from, Mati supports their IDs.

The forms of identification that we support are:

You can select one or more documents to ask to your users. These can be as a sequence of different required documents or you can select more than one per step so your users can choose the one they currently have.

A flow with different document options A flow with different document options

Additionally, you can add biometric verification to your flow, so that your user's face is matched with the photo in the identity card.

The forms of identification that we support are:

Both types of biometric will trigger a face-match check.

Web SDK Overview

Our web SDK is executed through a customizable button. After you have configured your verification flow it will automatically synchronize through your clientID from your dashboard and adapt to the settings you have selected there.

Watch the following video for a detailed explanation of the features of our Web SDK. https://youtu.be/yD3zeC5WiO4

Note that you should pass a metadata string as shown above, so you can know which user is being verified. This metadata string will be sent in all our webhook callbacks regarding to that verification in particular.

In your metadata strings you can pass:

Also you can watch our web SDK integration logic tutorial below so you better understand how to configure your dashboard, use our button and how to interpret our webhook callbacks. https://www.youtube.com/watch?v=Nmug8bynLN4&t=0s You can also send a link to your users containing metadata strings, no website needed!

For example

For more information regarding our button specifically please go to our Github page.

iOS/Android SDKs integration

You can also verify your users through the usage of our mobile SDKs. See the following tutorial for full details.

https://www.youtube.com/watch?v=Tx4ZJ8AAkZA&t=1s

iOS

Android

Other integrations

Webhooks - verification callbacks

For this section we recommend you to review our flowchart document so that you have a clear visual representation of all the events that we will send you. Additionally, check out our video tutorial below.

Configure your webhook URL

Configure an URL to which Mati will send your user's webhooks in your dashboard inside the SDKs Integration tab. webhookSection

Example of a decoding script (javascript)

    const crypto = require('crypto');

    // A sample webhook coming from Mati
    const WEBHOOK_PAYLOAD = 
    {   
        eventName: 'verification_completed',
        metadata: {email: 'john@gmail.com'},
        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);

You have to set a webhook secret that we will use to hash out on of the headers in our webhooks, by decoding it using your webhook secret and comparing the decoded string you can always make sure that the webhook is coming from Mati and not another party.

For each verification that Mati has finished processing (via SDKs or API), Mati will send you a verification_completed webhook. Note that this webhook contains the key matiDashboardUrl which you can use to directly access a given verification through your browser.

All webhooks contain a resource URL that has all the user's verification information. Note that you do not have to wait for the verification_completed webhook to consult it. Just GET it! More information about this request in our 5 Retrieve verification data section.

For every check that Mati performs, we will send you a corresponding webhook containing the result of said check.

The webhooks that we send are:

Webhook Description
verification_started Sent at the beginning of the SDKs flow, when Mati s making a new verification record (usually at the upload of the first ID document)
verification_inputs_completed 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 Sent from your Mati dashboard manually after updating the user verification information.
verification_completed 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 user.
verification_expired Sent when verification is not completed after 30 minutes. It means that the user probably did not finish verification flow.
step_completed Webhook sent after each verification step is completed (liveness, face match, document-reading, alteration-detection, template-matching, watchlist)

Outcome of verification process - statuses and detailed reasons

There are three statuses of identity that processed automatically:

Webhook example

{
  "eventName" : "step_completed",
  "step" : {
    "status" : 200,
    "id" : "alteration-detection",
    "error" : {
      "type" : "StepError",
      "code" : "alterationDetection.fraudAttempt",
      "message" : "Document is considered as fraud attempt"
    }
  },
  "resource": "https://api.getmati.com/v2/verifications/601142c648494064cdd70d9a",
  "timestamp": "2021-01-27T10:39:12.348Z",
  "flowId": "5e972984c2b5b0001a8eac13"
}
{
  "eventName": "step_completed",
  "step": {
    "status": 200,
    "id": "alteration-detection",
    "error": {
      "type": "StepError",
      "code": "alterationDetection.negligence",
      "message": "Document is considered as negligence"
    }
  },
  "resource": "https://api.getmati.com/v2/verifications/601142c648494064cdd70d9a",
  "timestamp": "2021-01-27T10:39:12.348Z",
  "flowId": "5e972984c2b5b0001a8eac13"
}

Alteration Detection step with fraud and rejected documents, that are unaccepted, contains the specified reason of rejection. Specified conditions to send to rejected status or to manual review::

Status Code Message Conditions
Rejected alterationDetection.fraudAttempt Document is considered as fraud attempt Digital Photo Replacement, Fake Image, Text Replacement, Manual Photo Replacement, Different Front And Back, Underage Person
Manual review alterationDetection.negligence Document is considered as negligence Physical Obstruction, Digital Obstruction, Blurred Image, Pixelated, Screen Photo, Black And White Image, Cropped Document, Distorted Photo, Same Photo Twice, Color Copy, Incorrect Document, No Document
Manual review watchlists.notEnoughParams No data to extract information Watchlist error
Manual review legacy.error We found a match inside a watchlist Watchlist error
Manual review curp.fullNameMismatch Full name mismatch Govcheck failure
Manual review curp.invalid CURP number is invalid Govcheck failure
Manual review ine.notFound No ine data found Govcheck failure
Manual review rfc.invalidParams CURP number is invalid Govcheck failure
Manual review legacy.error The face did not match the document Facematch too low
Manual review templateMatching.noMatchFound Document did not match any valid template Template Matching error
Manual review value: null none Document reading error (not all required fields obtained)

Verification step errors

Note that if an error occurs in any of the verification steps we will send error key within the webhook with details about what went wrong.

Another example would be the case in which the user uploaded a document that is partially or completely unreadable. In this case Mati will return null for all data.

In the case where the user verification has issues (ex: Mati could not read the ID document), we highly recommend that you manually check that user. That should not happen often, but you need to take it into account.

Find an exhaustive list of errors, messages and their description in this document.

Automating your flow with webhooks

After you have received the verification webhooks it is up to you to build rules to automate your user verification flow. (See our example integration logic inside our flowchart document

There is some little delay between the user finishing the Mati SDK, and Mati finishing to verify the user (usually a couple of minutes). During that time, we recommend that you display to your users a screen saying "We are verifying your identity, please come back in a few minutes". (Or any other business logic you would like to build with the information in our webhooks)

En example of an integrated verification process:

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 there aren't any error 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

API Overview

This Postman collection allows you to explore the usage of the Mati API, so you can easily understand how to implement it in your own backend. It is important to note that these request should be performed sequentially so that the user verification process is successful. For step 3 send-input, 3 different configuration examples are shown for the convenience of the reader, step 3 should only be performed once per verification.

You can watch a video explanation of our API here:

API Description

POST 1. Get API Access Token.

Get Access Token

curl --location --request POST 'https://api.getmati.com/oauth' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "Authorization: Basic \
$(echo -en 'YOUR_MATI_CLIENT_ID:YOUR_MATI_CLIENT_SECRET' | base64)" \
-d 'grant_type=client_credentials' \

Response

{
  "access_token": "YOUR_MATI_ACCESS_TOKEN",
  "expiresIn": 3600,
  "token_type": "bearer"
}

Authorization: Basic Authentication

Description

With this request you will be able to obtain an access token (JWT) and use it to access to Mati's verification process scope.

You should send Authorization header that is a string concatenated with colons (YOUR_MATI_CLIENT_ID:YOUR_MATI_CLIENT_SECRET) and encoded into base64.

Both YOUR_MATI_CLIENT_ID and YOUR_MATI_CLIENT_SECRET are available through your dashboard in the SDKs integration tab.

Headers

Parameter Required Value
Content-Type true application/x-www-form-urlencoded
Authorization true Basic YOUR_MATI_AUTHORIZATION_TOKEN
User-Agent false YOUR_CLIENT_USER_AGENT
X-Forwarded-For false YOUR_CLIENT_IP_ADDRESS

Body

Parameter Type Required Value
grant_type string true client_credentials

Response

Parameter Type Description
access_token string An access token used to access API
expires_in number Access token expiration time in seconds
scope string Space separated requested access scope
token_type string Received token type

Errors

Status Code Error Code Message
400 invalid_scope Invalid scope: Requested scope is invalid
400 unsupported_grant_type Unsupported grant type: grant_type is invalid
401 invalid_client Invalid client: client is invalid

POST 2. Create Verification.

Create Verification

curl --location --request POST 'https://api.getmati.com/v2/verifications' \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer YOUR_MATI_ACCESS_TOKEN" \
-d '{
        "flowId": "YOUR_MATI_FLOW_ID",
        "metadata": {
            "user": "JOHN MALCOVICH",
            "id": "123e4567-e89b-12d3-a456-426614174000"
        }
    }'

Response

{
    "id": "YOUR_MATI_VERIFICATION_ID",
    "documents": [],
    "expired": false,
    "flow": {
        "id": "YOUR_MATI_FLOW_ID",
        "name": "Flow name"
    },
    "identity": "YOUR_MATI_IDENTITY_ID",
    "inputs": [
        {
            "id": "connection-data",
            "status": 200
        },
         {
            "id": "selfie-video",
            "status": 0
        }
    ],
    "steps": [
        {
            "status": 0,
            "id": "liveness"
        }
    ]
}

Authorization: Bearer Authentication

Description

With this request you will be able to create new verification.

You have to specify flowId when creating verification. flowId can be found in your dashboard in the SDKs integration tab. When flowId parameter omitted your Default flow will be used instead.

Pass IP address of end-user through header param X-Forwarded-For. It would help us to provide ip-validation step.

Headers

Parameter Required Value
Content-Type true application/json
Authorization true Bearer YOUR_MATI_ACCESS_TOKEN
User-Agent false YOUR_END_USER_USER_AGENT
X-Forwarded-For false YOUR_END_USER_IP_ADDRESS

Body

Parameter Type Required Value
flowId string false YOUR_MATI_FLOW_ID
metadata object false YOUR_REQUEST_METADATA

Response

Parameter Type Description
id string Verification ID used to access your verification
documents array An array of uploaded documents (empty)
expired boolean A flag that shows do verification expired or no
flow object An object that represents flow options
identity string Identity ID
inputs array An array of inputs
steps steps An array of root steps

POST 3. Send Input example: (BR National ID, No Biometrics)

Send Input(BR National ID, no biometrics)

curl --location --request POST 'https://api.getmati.com/v2/identities/{{_id}}/send-input' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer {{access_token}}' \
--form 'inputs=[ \
    {"inputType":"document-photo","group":0, \
        "data":{"type":"national-id","country":"BR","region":"","page":"front","filename":"43_front.jpg"}}, \
    {"inputType":"document-photo","group":0, \
        "data":{"type":"national-id","country":"BR","region":"","page":"back","filename":"44_back.jpg"}} \
    ]' \
--form 'document=@/C:/PATH_TO_FILE/43_front.jpg' \
--form 'document=@/C:/PATH_TO_FILE/44_back.jpg'

Send Input Successful Response

[
  {
    "result": true
  },
  {
    "result": true
  }
]

Error in "inputs" value

{
  "code": 400,
  "message": "No input strategy found for document-photo"
}

With this POST request you will be able to send the user's fields to its identity (images of document - front/back, selfie image, liveness video). Make sure that you configured your verification flow on your Mati dashboard (both document and biometrics requirements)

Authorization: Bearer Token

It is very important that the JSON in inputs matches the flow configuration on your dashboard. Otherwise you will not be able to send this request.

In inputs you will need to specify the country of origin of the ID document. Use ISO-2 codes.

If you verify users from the United States you will also have to specify the region (state):

Otherwise you can leave region:"" empty.

Use document to upload the user's files, so they can be verified.

Mati API will get your verification configuration, wait for all inputs needed, and start the verification process once it received all needed inputs.

HEADERS
Authorization Bearer {{access_token}}
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 in section above.
page Page id to upload. Any of ['front', 'back']
filename Original file name
BODY formdata
inputs [{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"BR","region":"","page":"front","filename":"43_front.jpg"}},{"inputType":"document-photo","group":0,"data":{"type":"national-id","country":"BR","region":"","page":"back","filename":"43_back.jpg"}}]
document
document
Response
result Set up to true if input validation passed, otherwise an error will present
error Error data. Contains ErrorType and ErrorCode
Error Type Description
IntegrationError in general case this kind of error happens when there are error in integrating of API.
ValidationError provided data do not pass through validation. User should upload other photo, or video files.
SystemError Errors related to the server infrastructure.
Error Code 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"
documentPhoto.badText Document field validation failed
documentPhoto.blurryText Document photo is too blurry
documentPhoto.smallImageSize Document photo has too small resolution
documentPhoto.unexpectedData Unexpected error in document reading happened
documentPhoto.noText Document photo has no text
documentPhoto.noFace Document photo has no face (in case if required)
documentPhoto.identicalImages Document photos for front and back sides are identical (for two-sided documents only)
documentPhoto.similarImages Document photos for front and back sides are similar (for two-sided documents only)
documentPhoto.grayscaleImage Document photo is a grayscale image
documentPhoto.screenPhoto Document photo is a screen photo
documentPhoto.noDocument Document photo has no matching document pattern
documentPhoto.missingFields Document photo is missing some of required fields
documentPhoto.wrongFormat Some required fields from document photo have invalid format
documentPhoto.noMrz Document photo has no Machine-Readable Zone (in case if required)
documentPhoto.badMrz Document photo has corrupted Machine-Readable Zone
documentPhoto.noPdf417 Document photo has no PDF417 barcode (in case if required)
documentPhoto.badPdf417 Document photo has corrupted PDF417 barcode
documentPhoto.typeMismatch Document type claimed by user and document type detected from document photo are different
documentPhoto.countryMismatch Document country claimed by user and document country detected from document photo are different
selfiePhoto.multipleFaces There are several faces detected on selfie photo
selfiePhoto.noFace No faces detected on selfie photo
selfieVideo.conversionFailed Video conversion failed (invalid file or upload failed)
selfieVideo.multipleFaces Multiple simultaneous faces are detected in the video
selfieVideo.noFace No faces detected in the video
voiceVideo.conversionFailed Video conversion failed (invalid file or upload failed)
voiceVideo.multipleFaces Multiple simultaneous faces are detected in the video
voiceVideo.noFace No faces detected in the video
system.internalError Something unexpected happened in the server.

POST 3. Send Input example: (MX NI + MX Passport + PoR + Selfie)

Example request

curl --location --request POST 'https://api.getmati.com/v2/identities/{{_id}}/send-input' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer {{access_token}}' \
--form 'inputs=[
  {
    "inputType": "document-photo",
    "group": 0,
    "data": {
      "type": "driving-license",
      "country": "MX",
      "page": "front",
      "filename": "MX_NI_FRONT.jpg"
    }
  },
  {
    "inputType": "document-photo",
    "group": 0,
    "data": {
      "type": "driving-license",
      "country": "MX",
      "page": "back",
      "filename": "MX_NI_BACK.jpg"
    }
  },
    {
    "inputType": "document-photo",
    "group": 1,
    "data": {
      "type": "passport",
      "country": "MX",
      "page": "front",
      "filename": "MX_PASSPORT.jpg"
    }
   {
    "inputType": "document-photo",
    "group": 2,
    "data": {
      "type": "proof-of-residency",
      "country": "MX",
      "page": "front",
      "filename": "proof-of-residence.png"
    }
  },
  {
    "inputType": "selfie-photo",
    "data": {
      "type": "selfie-photo",
      "filename": "selfie-photo.jpg"
    }
  }
]' \
--form 'document=@/C:/PATH_TO_FILE/MX_NI_FRONT.jpg' \
--form 'document=@/C:/PATH_TO_FILE/MX_NI_BACK.jpg' \
--form 'document=@/C:/PATH_TO_FILE/MX_PASSPORT.jpg' \
--form 'document=@/C:/PATH_TO_FILE/proof-of-residence.png' \
--form 'selfie=@/C:/PATH_TO_FILE/selfie-photo.jpg'

Bad input structure

{
  "code": 400,
  "message": "Invalid structure of request body",
  "name": "MoleculerError"
}

Inconsistency with dashboard

[
  {
    "error": {
      "code": "validation.error"
    }
  },
  {
    "error": {
      "code": "validation.error"
    }
  },
  {
    "result": true
  },
  {
    "result": true
  },
  {
    "result": true
  }
]

Media incorrectly specified

[
  {
    "error": {
      "code": "media.not.found"
    }
  },
  {
    "error": {
      "code": "media.not.found"
    }
  },
  {
    "error": {
      "code": "media.not.found"
    }
  }
]

Invalid input

{
  "code": 400,
  "data": {
    "body": {},
    "error": {}
  },
  "message": "Invalid request body",
  "name": "InvalidRequestBodyError",
  "type": "INVALID_REQUEST_BODY"
}
HEADERS
Authorization Bearer {{access_token}}
Body formdata
inputs [ { "inputType": "document-photo", "group": 0, "data": { "type": "national-id", "country": "MX", "page": "front", "filename": "MX_NI_FRONT.jpg" } }, { "inputType": "document-photo", "group": 0, "data": { "type": "national-id", "country": "MX", "page": "back", "filename": "MX_NI_BACK.jpg" } }, { "inputType": "document-photo", "group": 1, "data": { "type": "passport", "country": "MX", "page": "front", "filename": "MX_PASSPORT.jpg" } }, { "inputType": "document-photo", "group": 2, "data": { "type": "proof-of-residency", "country": "MX", "page": "front", "filename": "proof-of-residence.png" } }, { "inputType": "selfie-photo", "data": { "type": "selfie-photo", "filename": "selfie-photo.jpg" } } ]
document
document
document
document
document

POST 3. Send Input example: (US DL, + Liveness video)

Example request

curl --location --request POST 'https://api.getmati.com/v2/identities/{{_id}}/send-input' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer {{access_token}}' \
--form 'inputs=[
  {
    "inputType": "document-photo",
    "group": 0,
    "data": {
      "type": "national-id",
      "country": "US",
      "region" : "CA",
      "page": "front",
      "filename": "US_CA_DL_FRONT.jpg"
    }
  },
  {
    "inputType": "document-photo",
    "group": 0,
    "data": {
      "type": "national-id",
      "country": "US",
      "region" : "CA",
      "page": "back",
      "filename": "US_CA_DL_BACK.jpg"
    }
  },
   {
    "inputType": "selfie-video",
    "data": {
      "filename": "Liveness_video.mp4"
    }
  }
]' \
--form 'document=@/C:/PATH_TO_FILE/US_CA_DL_FRONT.jpg' \
--form 'document=@/C:/PATH_TO_FILE/US_CA_DL_BACK.jpg' \
--form 'video=@/C:/PATH_TO_FILE/Liveness_video.mp4'

Successful

[
  {
    "result": true
  },
  {
    "result": true
  },
  {
    "result": true
  }
]

Partial successful

[
  {
    "error": {
      "code": "validation.error"
    }
  },
  {
    "error": {
      "code": "media.not.found"
    }
  },
  {
    "result": true
  }
]
HEADERS
Authorization Bearer {{access_token}}
Body formdata
inputs [ { "inputType": "document-photo", "group": 0, "data": { "type": "national-id", "country": "US", "region" : "CA", "page": "front", "filename": "US_CA_DL_FRONT.jpg" } }, { "inputType": "document-photo", "group": 0, "data": { "type": "national-id", "country": "US", "region" : "CA", "page": "back", "filename": "US_CA_DL_BACK.jpg" } }, { "inputType": "selfie-video", "data": { "filename": "Liveness_video.mp4" } } ]
document
document
document

GET 4. Retrieve Webhook Resource Data

Retrieve verification data - Successful

curl --location --request GET 'https://api.getmati.com/api/v1/verifications/5e27a34309b87d001bb2ca0d' \
--header 'Content-Type: application/x-www-form-urlencoded'
{
  "expired": false,
  "deviceFingerprint": {
    "ua": "Mozilla/5.0 (Linux; Android 10; SM-M305M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36",
    "browser": {
      "name": "Chrome",
      "version": "79.0.3945.116",
      "major": "79"
    },
    "engine": {
      "name": "WebKit",
      "version": "537.36"
    },
    "os": {
      "name": "Android",
      "version": "10"
    },
    "device": {
      "vendor": "Samsung",
      "model": "SM-M305M",
      "type": "mobile"
    }
  },
  "identity": {
    "status": "reviewNeeded"
  },
  "steps": [
    {
      "status": 200,
      "id": "selfie",
      "data": {
        "selfiePhotoUrl": "https://media.getmati.com/media/eyJhbGciOiJIUzI1NiIsInR5cC..."
      },
      "error": null
    }, {
        "status": 200,
        "id": "ip-validation",
        "data": {
            "country": "Mexico",
            "countryCode": "MX",
            "region": "Chihuahua",
            "regionCode": "CHH",
            "city": "Ciudad Juárez",
            "zip": 32472,
            "latitude": 1.713,
            "longitude": 106.39,
            "safe": false
        }
    }
  ],
  "documents": [
    {
      "country": "MX",
      "type": "national-id",
      "steps": [
        {
          "status": 200,
          "id": "mexican-curp-validation",
          "error": {
            "type": "StepError",
            "code": "curp.invalid",
            "message": "CURP number is invalid"
          }
        },
        {
          "status": 200,
          "id": "mexican-ine-validation",
          "error": {
            "type": "StepError",
            "code": "ine.notEnoughParams",
            "message": "Not enough params to get ine data"
          }
        },
        {
          "status": 200,
          "id": "template-matching",
          "error": {
            "type": "StepError",
            "code": "templateMatching.noMatchFound",
            "message": "Document did not match any valid template"
          }
        },
        {
          "status": 200,
          "id": "facematch",
          "error": {
            "type": "LegacyError",
            "code": "legacy.error",
            "message": "The face did not match the document"
          }
        },
        {
          "status": 200,
          "id": "document-reading",
          "error": null,
          "data": {
            "fullName": {
              "value": null,
              "label": "Name"
            },
            "address": {
              "value": null,
              "label": "Address"
            },
            "documentNumber": {
              "value": null,
              "label": "Document Number"
            },
            "dateOfBirth": {
              "value": null,
              "label": "Day of Birth",
              "format": "date"
            },
            "expirationDate": {
              "value": null,
              "label": "Date of Expiration",
              "format": "date"
            },
            "cde": {
              "value": null,
              "label": "Elector Key"
            },
            "curp": {
              "value": null,
              "label": "CURP"
            },
            "ne": {
              "value": null,
              "label": "Emission Number"
            },
            "ocrNumber": {
              "value": null,
              "label": "OCR Number"
            }
          }
        },
        {
          "status": 200,
          "id": "alteration-detection",
          "error": {
            "type": "LegacyError",
            "code": "legacy.error",
            "message": "Document is considered tempered"
          }
        },
        {
          "status": 200,
          "id": "watchlists",
          "error": {
            "type": "LegacyError",
            "code": "legacy.error",
            "message": "No data to extract information"
          }
        }
      ],
      "fields": {
        "fullName": {
          "value": null,
          "label": "Name"
        },
        "address": {
          "value": null,
          "label": "Address"
        },
        "documentNumber": {
          "value": null,
          "label": "Document Number"
        },
        "dateOfBirth": {
          "value": null,
          "label": "Day of Birth",
          "format": "date"
        },
        "expirationDate": {
          "value": null,
          "label": "Date of Expiration",
          "format": "date"
        },
        "cde": {
          "value": null,
          "label": "Elector Key"
        },
        "curp": {
          "value": null,
          "label": "CURP"
        },
        "ne": {
          "value": null,
          "label": "Emission Number"
        },
        "ocrNumber": {
          "value": null,
          "label": "OCR Number"
        }
      },
      "photos": [
        "https://media.getmati.com/media/eyJhbGciOiJIUzI1NiIs...",
        "https://media.getmati.com/media/eyJhbGciOiJIUzI1NiIs..."
      ]
    }
  ],
  "hasProblem": true,
  "computed": {
    "age" : {
      "data" : 33
    },
    "isDocumentExpired" : {
      "data" : {
        "national-id" : false,
        "passport": true,
        "proof-of-residency": null
       }
    }
  },
  "id": "5e2a838309be7d001bb2ca0d"
}

Use the JWT access_token obtained in 1. Get API Authorization Token the user's information after they have submitted their documents or completed the verification via our Web SDK.

The endpoint used is the one contained in the Mati Webhook as resource.

For example:

HEADERS
Content-Type application/x-www-form-urlencoded
Authorization Bearer {{access_token}}

Steps

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)
ip-validation Information about user's location and validation if proxy is used
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)
mexican-ine-validation For certain countries only (only Mexico supported for now)
mexican-rfc-validation For certain countries only (only Mexico supported for now)
argentinian-renaper-validation For certain countries only (only Argentina supported for now)

Computed

Computed section is additional verification data which based on received data

Computation Description Type
age Information about user's age. It based on the first document with date of birth field Number
isDocumentExpired User's documents expiration information. Documents marked true are expired. If there is no expiration date found in document null will be set up instead Boolean | null

GET 5. Download Verification Media

curl --location --request GET 'https://media.getmati.com/media/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJodHRwOi...' \
--header 'Authorization: Bearer {{access_token}}'

Use the JWT access_token obtained in 1. Get API Authorization Token to download any the user's verification images. This is ony possible after you have done step 5 since you have to use an the URL contained in the verification profile.

HEADERS
Authorization Bearer {{access_token}}

POST [Optional] Email Validation

curl --location --request POST 'https://api.getmati.com/safety/v1/checks/email' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "invalid@mati.io"
}'

Valid and deliverable email

{
  "data": {
    "deliverable": "true",
    "safe": "true"
  }
}

Incorrect email format

{
  "name": "ValidationError",
  "message": "data/email should match format \"email\"",
  "code": 422,
  "type": "VALIDATION_ERROR",
  "data": [
    {
      "keyword": "format",
      "dataPath": "/email",
      "schemaPath": "#/properties/email/format",
      "params": {
        "format": "email"
      },
      "message": "should match format \"email\""
    }
  ]
}

Unsafe email

{
  "data": {
    "deliverable": "unknown",
    "safe": "false"
  }
}

Unexpected error

{
  "error": {
    "code": 500,
    "message": "Something went wrong, please try again later."
  }
}

With this POST request you will be able to ensure that your user's email are both safe and deliverable. Use the JWT access_token obtained in 1. Get API Authorization Token

HEADERS
Authorization Bearer {{access_token}}
Content-Type application/json

You can send an email address as a JSON and the API will return two parameters.

Param Description
Deliverable It checks for the availability of the mailbox. If it is true then the mailbox has confirmed to be available. false indicates an inexistent mailbox and unknown means that the existence of the mailbox cannot be determined.
Safe if the email is deliverable an if it is not a disposable or spam account.
BODY raw

POST [Optional] Comply Advantage Validation

curl --location --request POST 'https://api.getmati.com/safety/v1/checks/comply-advantage' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "Full Name",
    "birthYear": 1970
}'

Match is not found in watchlists

{
  "data": {
    "searchedOn": "2020-05-13 09:26:50",
    "nameSearched": "Full Name",
    "profileUrl": "https://public.url",
    "error": null
  }
}

Match is found in watchlists

{
  "data": {
    "searchedOn": "2020-05-13 09:26:50",
    "nameSearched": "Full Name",
    "profileUrl": "https://public.url",
    "error": {
      "type": "SafetyCheckError",
      "code": "complyAdvantage.matchFound",
      "details": {
        "matchStatus": "potential_match",
        "riskLevel": "unknown",
        "numberOfBlacklists": 0
      }
    }
  }
}

With this POST request you will be able to ensure that your users are compliant. Use the JWT access_token obtained in 1. Get API Authorization Token

Request Headers Description
Authorization Bearer {{access_token}}
Content-Type application/json
Request Body Params Description Type
name Full name of a person String. Required
birthYear Birth year of a person Number. Required
Response Body Params Description Type
data.searchedOn Date when search's done Date
data.nameSearched Name searched by String
data.profileUrl URL to public profile String. URL
data.error Error. null in case match not found Object. Nullable
data.error.type Error type String
data.error.code Error code String
data.error.details.matchStatus Error match status String
data.error.details.riskLevel Error risk level String
data.error.details.numberOfBlacklists Error number of blacklists Number

cation`.

You can specify flowId when creating identity. flowId can be found in your dashboard in the SDKs integration tab. When flowId parameter omitted your Default flow will be used instead. Pass IP address of end user through header param X-Forwarded-For, optional.

Headers
Parameter Required Value
Content-Type true application/json
Authorization true Bearer YOUR_MATI_ACCESS_TOKEN
User-Agent false YOUR_CLIENT_USER_AGENT
X-Forwarded-For false YOUR_CLIENT_IP_ADDRESS
Body
Parameter Type Required Value
flowId string false YOUR_MATI_FLOW_ID
metadata object false YOUR_CLIENT_METADATA
HEADERS mandatory/optional
Authorization Bearer {{access_token}} mandatory
Content-Type application/json mandatory
'X-Forwarded-For' IP of End User optional
BODY raw