API Reference

Introduction

Sum&Substance's API allows you to send and receive applicants’ data and documents for verification through simple RESTful APIs. API responds with JSON payload (unless otherwise stated). When data payload should be provided, it expects parameters to be passed in valid JSON (except when stated otherwise) with the Content-Type: application/json header being set.

All incompatible changes made to API will be versioned and won't affect existing endpoints. However, we may add additional fields to JSON response without any notice or API versioning. So it's not a good idea to rely on object mappers in your integration code that will fail on unknown properties.

We have two environments:

  • Test environment test-api.sumsub.com is only needed to implement integration with Sum&Substance. No checks are performed on the test environment.
  • In order to provide you with access to the production environment api.sumsub.com we require that the integration is tested first. We just want to make sure that everything goes right.

All example requests can be inserted into the command line or Postman to check the response from the endpoints.

Authentication

All API queries must be made over HTTPS, and plain HTTP will be refused. You must include your Authorization header in all requests.

OAuth 2.0 authentication

To make requests from your backend to our API you need to obtain a token via API using the username and password that were given to you.

POST /resources/auth/login

REQUEST HEADERS
Name Type Description
Authorization String Basic base64

Where base64 is a base64-encoded concatenation of the username:password. See Wikipedia for more details.

RESPONSE
Name Type Description
payload String A newly generated access token
status String Token generation result

Once you have received the bearer token (payload), you can use the API with OAuth 2.0 authorization.

The bearer token (payload) is valid only for a limited time. (By default, 7 days) It is best not to tie for the lifetime of the token, but to generate a new one when receiving 401 HTTP response code. We recommend to cache the token for a certain amount of time and generate a new one if it is expired or no longer valid.

Using base64 (username:password) to obtain bearer token (payload) request example
Bearer token (payload) obtaining response example
Using bearer token to make a request

Invalidating OAuth tokens

Invalidate a particular token:

POST /resources/auth/logout

Invalidate all tokens that are associated with the current account:

POST /resources/auth/logoutAll

REQUEST HEADERS
Name Type Description
Authorization String Bearer $token
RESPONSE

No content 204 is returned when all is fine.

Access tokens for SDKs

When initializing WebSDK or MobileSDK, an access token authentication must be used.

POST /resources/accessTokens

REQUEST HEADERS
Name Type Value
Accept String application/json
Authorization String OAuth2 token
REQUEST ARGUMENTS
Name Type Required Description
ttlInSecs Integer No Lifespan of a token in seconds. Default value is equal to 10 mins.
userId String Yes An external user ID which will be bound to the token
applicantId String No Applicant ID that should be bound to this token
RESPONSE
Name Type Description
token String A newly generated access token for applicant

An access token for applicant has limited access to the API, e.g. it’s only valid for 1 applicant and can't access other applicants.

Creating an access token for applicant
Example response

Errors

On API errors we return standard HTTP Status Codes. Response body contains a JSON payload with additional information. For example:

Response body
Name Type Always present Description
description String Yes Human-readable error description
code Integer Yes HTTP Status code
correlationId String Yes This id uniquely identifies the error. You can send this id to us, in case the cause of the problem is still unclear
errorCode Integer No Code of the exact problem (see the Error codes section). For some errors may not be present
errorName String No String representation of the exact problem (see the Error codes section). It's always present when errorCode is

Error codes

Please, note that not all error responses will contain error code and name. More and more errors will get their codes in the future.

Code Name Description
1000 duplicate-document Duplicate document (image, video) was uploaded. Exact equality is taken into account
1001 too-many-documents Applicant contains too many documents. Adding new ones is not allowed.
1002 file-too-big Uploaded file is too big (more than 64MB)
1003 empty-file Uploaded file is empty (0 bytes)
1004 corrupted-file File is corrupted or incorrect format (e.g. PDF file is uploaded as JPEG)
1005 unsupported-file-format Unsupported file format (e.g. a TIFF image)

If you still don't understand what the issue is, please, contact us with the correlationId provided.

Applicants API

Creating an applicant

An applicant is an entity representing one physical person. It may have several ID documents attached, like an ID card or a passport. Many additional photos of different documents can be attached to the same applicant.

POST /resources/applicants

When creating the applicant, the request body should contain parameter requiredIdDocs. You can have any combination of docSets in any order (this will be automatically mapped to the corresponding WebSDK steps). The only limitation is that document types must not overlap between different docSets.

When creating an applicant, you don't have to send us prefilled applicant data such as names, dob, addresses, etc. You only need to send us images and get the recognized document data by this method. It's the best way to increase your conversion because rejects of mismatches in names and typos will be excluded.

REQUEST HEADERS
Name Type Value
Content-Type String application/json
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
#{body} Hash Yes An object representing an applicant (see example)
REQUEST BODY
Name Type Required Description
externalUserId String No An applicant ID on the client side, should be unique
sourceKey String No If you want to separate your clients that send applicants, provide this field to distinguish between them
email String No Applicant email
lang String No The language in which the applicant should see the result of verification
metadata Array of strings No Additional information that is not displayed to the end user ( Example: [{"key": "keyFromClient", "value": "valueFromClient"}] )
requiredIdDocs Hash No Description of the required document set
info Hash Yes Basic information about the applicant
info ATTRIBUTE
Name Type Required Description
firstName String No First name
lastName String No Last name
middleName String No Middle name
legalName String No Legal name
gender String No Sex of a person
dob String No Date of birth (format YYYY-mm-dd, e.g. 2001-09-25)
placeOfBirth String No Place of birth
countryOfBirth String No Country of birth
stateOfBirth String No State of birth
country String No Alpha-3 country code (e.g. DEU or RUS) (Wikipedia)
nationality String No Alpha-3 country code (Wikipedia)
phone String No Phone number
mobilePhone String No Mobile phone number
addresses Array No List of addresses
addresses ELEMENTS FIELDS
Name Type Required Description
country String No 3-letter country code
postCode String No Postal code
town String No Town or city name
street String No Street name
subStreet String No Additional street information
state String No State name if applicable
buildingName String No Building name if applicable
flatNumber String No Flat or apartment number
buildingNumber String No Building number
startDate String No Start date of stay in current building (format YYYY-mm-dd, e.g. 2001-09-25)
endDate String No End date of stay in current building (format YYYY-mm-dd, e.g. 2002-10-26)

An applicant entity with the associated id (refer to the example). Persist the id field after creating the applicant - it will be needed for further requests that you intend to make.

For the IDENTITY step we recommend the following document types: PASSPORT, ID_CARD, RESIDENCE_PERMIT, DRIVERS

Supported idDocSetType are:
Value Description
IDENTITY A document that identifies an applicant
IDENTITY2 An additional document that identifies an applicant
SELFIE Some sort of facial photo of an applicant
SELFIE2 Some sort of facial video of an applicant (please use this when you ask users to upload a video selfie)
PROOF_OF_RESIDENCE Proof of address, something like a utility bill (e.g. internet or electricity bills)
PAYMENT_METHODS The payment method as credit card, crypto-wallet or other

Other types can be found in the webSDK demo.

Example request
Example response

Changing required document set

This method patches the required document set of the applicant. In case you need to add one more step to the check, for example, only the id document and the selfie were requested at first, and after the check has been completed you need to get the proof of address, so you need to add one more step to the current list of required documents. Please note that you should have special permission to make this request (contact the Sum&Substance tech team).

POST /resources/applicants/{applicantId}/requiredIdDocs

REQUEST HEADERS
Name Type Value
Content-Type String application/json
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
body Hash No A new document set required
RESPONSE

A new requiredIdDocs will be returned.

Example request without payload for getting current required document set
Example request with payload for changing required document set
Example response

Adding an ID document

A method gets a multipart form: ID doc JSON metadata and, optionally, a document photo. If the ID doc with this type already exists, its data will be merged. Existing data will be overwritten if they also present in the new object. However, a new image will be added nonetheless. If document metadata are not known yet, just send type and a country. E.g. "PASSPORT" and "GBR". These two fields are mandatory.

POST /resources/applicants/{applicantId}/info/idDoc

REQUEST HEADERS
Name Type Value
Content-Type String multipart/form-data
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
FORM DATA
Name Type Required Description
metadata Hash Yes An object representing an ID document
content Binary No A photo of a document
REQUEST metadata BODY PART FIELDS
Name Type Required Description
idDocType String Yes See supported document types here
idDocSubType String No FRONT_SIDE, BACK_SIDE or null
country String Yes 3-letter country code (Wikipedia)
firstName String No First name
middleName String No Middle name
lastName String No Last name
issuedDate String No Issued date (format YYYY-mm-dd, e.g. 2001-09-25)
validUntil String No Valid until date (format YYYY-mm-dd, e.g. 2001-09-26)
number String No Document number
dob String No Date of birth
placeOfBirth String No Place of birth
RESPONSE

JSON representing added document information.

If you need to know the imageId of the photo, you can find this information in the response header X-Image-Id

Example request
Example response

In cases when only the data of the document needs to upload:

Example response

Supported document types

Value Description
ID_CARD An ID card
PASSPORT A passport
DRIVERS A driving license
BANK_CARD A bank card, like Visa or Maestro
UTILITY_BILL A utility bill
BANK_STATEMENT A bank statement
SELFIE A selfie with a document (in this case no additional metadata should be sent)
VIDEO_SELFIE A selfie video (can be used in webSDK or mobileSDK)
PROFILE_IMAGE A profile image, i.e. userpic (in this case no additional metadata should be sent)
ID_DOC_PHOTO Photo from an ID doc (like a photo from a passport) (No additional metadata should be sent)
AGREEMENT Agreement of some sort, e.g. for processing personal info
CONTRACT Some sort of contract
RESIDENCE_PERMIT Residence permit or registration document in the foreign city/country
EMPLOYMENT_CERTIFICATE A document from an employer, e.g. proof that a user works there
DRIVERS_TRANSLATION Translation of the driving license required in the target country
INVESTOR_DOC A document from an investor, e.g. documents which disclose assets of the investor
VEHICLE_REGISTRATION_CERTIFICATE Certificate of vehicle registration
INCOME_SOURCE A proof of income
PAYMENT_METHOD Entity confirming payment (like bank card, crypto wallet, etc)
OTHER Should be used only when nothing else applies

Getting applicant data

During the verification we also extract data from the applicant's id docs. To get the full structured view of an applicant you should perform the following request.

GET /resources/applicants/{applicantId}

or

GET /resources/applicants/-;externalUserId={externalUserId}

You may want to use the second request when you don't know an applicant id yet. E.g. when WebSDK created an applicant for you and we called your webhook endpoint.

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant id
externalUserId String Yes User id in your system
RESPONSE

The existence of each field depends on the documents submitted for verification and the regulations of verification.

It's basically an applicant that you've created (or we've created for you, e.g. via an WebSDK) with augmented information in cases where a verification has been completed. The info.idDocs field contains information extracted from applicants’ documents. As the described endpoint is actually quite generic (e.g. may return several applicants by external id, but we omit such details in this document), it returns a list of items. However, if you provide an applicant id that exists, only 1 item will be returned.

If an applicant ID does not exist, the endpoint won't return 404, instead it will return an empty list.

Example request
Example response

Here you can find examples of recognition results for all supported document types.

Changing applicant data

If you want to change an applicant's data you can issue a PATCH request instead of creating a new applicant, which is highly discouraged. This method patches the fields in the info key of the applicant. Please note that you should have special permissions to make this request (contact the Sum&Substance tech team).

PATCH /resources/applicants/{applicantId}/info

REQUEST HEADERS
Name Type Value
Content-Type String application/json
Authorization String Bearer token
REQUEST ARGUMENTS

The body must contain only those fields that you intend to change. Null fields will be ignored. If you want to unset some fields, provide these in the unsetFields query parameter. Check the info attribute above for the list of supported fields.

Name Type Required Description
#{body} Hash No Field in the info attribute that should be changed
applicantId String Yes Applicant ID
unsetFields String No Comma-separated list of fields to unset
RESPONSE

A patched info entity. As this endpoint could be quite damaging if misused, please check that it returns expected results on the test environment — it's a client's responsibility to treat their applicants well 😃

Changing applicant data will cause the applicant to be switched back to the pending state.

Example request
Example response

Getting verification results

When we are talking about verification results we typically mean two labels:

Value Description
GREEN Everything is fine
RED Some violations found

Once verification is finished, we will send you a POST request with JSON payload to the URL provided to us while integrating. Oftentimes, there are different URLs for staging and production environments. We may send several types of webhooks, but the one of interest here is the applicantReviewed webhook. This type of webhook is expected to arrive on average after 3-5 minutes, but can take up to 24 hours, in theory.

Although the webhook mechanism is a very convenient way to get notified about results as soon as possible on daily basis, it should not be completely relied upon. E.g. some network failure may occur, or, as we see from experience, some error conditions on the receiving side may cause the webhook request to fail. Of course, we log everything to help you debug the problem in the easiest way, however, we strongly encourage you to implement a polling mechanism by calling our API explicitly as a backup in cases where you think you’ve missed a webhook (e.g. you didn't receive a result after 1 hour). Currently, we don't attempt to send a webhook several times until they succeed, so having a polling mechanism as backup method is a proper way to ultimately ensure that you get the latest applicant status.

We recommend that you wait for the webhook no more than a day, and then send a request to our server for information about the status of the applicant.

Please note that the rejectLabels are only needed to analyze your statistics. They are generalized, so you should not use them to generate comments for your end user.

Webhook contains a reviewResult field that contains extra information, please see example. An important field is reviewAnswer, which contains a final highly trusted answer. Additionally, in cases of the RED answer, the rejectLabels field contains one or more of the following machine-readable constants:

Value Description
FORGERY Forgery attempt has been made
DOCUMENT_TEMPLATE Documents supplied are templates, downloaded from internet
LOW_QUALITY Documents have low-quality that does not allow definitive conclusions to be made
SPAM An applicant has been created by mistake or is just a spam user (irrelevant images were supplied)
NOT_DOCUMENT Documents supplied are not relevant for the verification procedure
SELFIE_MISMATCH A user photo (profile image) does not match a photo on the provided documents
ID_INVALID A document that identifies a person (like a passport or an ID card) is not valid
FOREIGNER When a client does not accept applicants from a different country or e.g. without a residence permit
DUPLICATE This applicant was already created for this client, and duplicates are not allowed by the regulations
BAD_AVATAR When avatar does not meet the client's requirements
WRONG_USER_REGION When applicants from certain regions/countries are not allowed to be registered
INCOMPLETE_DOCUMENT Some information is missing from the document, or it's partially visible
BLACKLIST User is blacklisted
UNSATISFACTORY_PHOTOS There were problems with the photos, like poor quality or masked information
DOCUMENT_PAGE_MISSING Some pages of a document are missing (if applicable)
DOCUMENT_DAMAGED Document is damaged
REGULATIONS_VIOLATIONS Regulations violations
INCONSISTENT_PROFILE Like woman's name with male's documents
PROBLEMATIC_APPLICANT_DATA Applicant data does not match the data in the documents
ADDITIONAL_DOCUMENT_REQUIRED Additional documents required to pass the check
AGE_REQUIREMENT_MISMATCH Age requirement is not met (e.g. cannot rent a car to a person below 25yo)
EXPERIENCE_REQUIREMENT_MISMATCH Not enough experience (e.g. driving experience is not enough)
CRIMINAL The user is involved in illegal actions
WRONG_ADDRESS The address from the documents doesn't match the address that the user entered
GRAPHIC_EDITOR The document has been edited by a graphical editor
DOCUMENT_DEPRIVED The user has been deprived of the document
COMPROMISED_PERSONS The user does not correspond to Compromised Person Politics
PEP The user belongs to the PEP category
ADVERSE_MEDIA The user was found in the adverse media
FRAUDULENT_PATTERNS The user was found in criminal reports
SANCTIONS The user was found on sanction lists
NOT_ALL_CHECKS_COMPLETED All checks were not completed
FRONT_SIDE_MISSING Front side of the document is missing
BACK_SIDE_MISSING Back side of the document is missing
SCREENSHOTS The user uploaded screenshots
BLACK_AND_WHITE The user uploaded black and white photos of documents
INCOMPATIBLE_LANGUAGE The user should upload translation of his document
EXPIRATION_DATE The user uploaded expired document
UNFILLED_ID The user uploaded the document without signatures and stamps
BAD_SELFIE The user uploaded a bad selfie
BAD_VIDEO_SELFIE The user uploaded a bad video selfie
BAD_FACE_MATCHING Face check between document and selfie failed
BAD_PROOF_OF_IDENTITY The user uploaded a bad ID document
BAD_PROOF_OF_ADDRESS The user uploaded a bad proof of address
BAD_PROOF_OF_PAYMENT The user uploaded a bad proof of payment
SELFIE_WITH_PAPER The user should upload a special selfie (e.g. selfie with paper and date on it)
OTHER Some unclassified reason

Finally, the reviewRejectType field tells about the rejection type (if the RED answer is given). The following values are possible:

Value Description
FINAL Final reject, e.g. when a person is a fraudster, or a client does not want to accept such kinds of clients in his/her system
RETRY A reject that can be fixed, e.g. by uploading an image of better quality
EXTERNAL When everything is fine with the submitted documents, but there are some external reasons to reject, like the avatar on the client's site does not meet the requirements
Example webhook payload with 'RED' answer
Example webhook payload with 'GREEN' answer

Applicant life-cycle

  • When creating an applicant, its status becomes init.
  • After uploading all required documents the status becomes pending.

Automatic status change (to pending) only works if a list of required documents is specified and all necessary documents are uploaded.

  • In some cases (discussed in advance), there is a special mode, when a client must manually signal to us when an applicant is ready to be reviewed.
  • After the verification process is completed, the status of the applicant will change to completed.

For clients who use the deprecated webhook mechanism, two more types can be returned:: if the webhook is delivered successfully (we received a 200 HTTP response code from your server), the status will be completedSent, otherwise completedSentFailure.

Getting applicant status (SDK)

It is recommended that you use this method if you are using a WebSDK.

GET /resources/applicants/{applicantId}/status

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
RESPONSE
Name Type Value
createDate Date Date of creation of the applicant
reviewDate Date Date of check ended
startDate Date Date of check started
moderationComment String A human-readable comment that can be shown to the end user
clientComment String A human-readable comment that should not be shown to the end user

The reviewStatus can be one of the following:

Value Description
init Initial registration has started. A client is still in the process of filling out the applicant profile. Not all required documents are currently uploaded.
pending An applicant is ready to be processed
queued The checks have been started for the applicant
completed The check has been completed
Example request
Example response

Getting applicant status (API)

It is recommended that you use this method if you not only need an applicant’s status, but also information about documents.

GET /resources/applicants/{applicantId}/state

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
RESPONSE

You will get JSON with fields:

Value Description
id Applicant ID
status General information on the applicant's verification
documentStatus Array which contains information on each checked document

Always displays information about the latest uploaded document.

Each element of the documentStatus array contains (after checking the document):

Value Description
idDocType Document type (See supported document types here)
country 3-letter country code Wikipedia
imageId Image ID which you can use to get the document
reviewResult Contains information about the success of the check. The information is available after the end of the document check.
moderationComment A human-readable comment that can be shown to the end user
clientComment A human-readable comment that should not be shown to the end user
Example request
Example response

Requesting an applicant re-check

You can programmatically ask us to re-check an applicant in cases where you or your user believe that our system made a mistake, or you want to request some additional checks agreed with us in advance. To do so you should explicitly move an applicant to the pending state by performing the following request.

POST /resources/applicants/{applicantId}/status/pending?reason=#{reason}

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
reason String No Status change reason

Provide a reason if available. It will help us understand why you want to recheck an applicant. If agreed in advance the reason you will be sending, we can automate the process and e.g. perform some additional checks and notify you about the results by the usual webhook means. If you want to automate complaints to your support, just send a user message along and we will consider it on a case-by-case basis.

RESPONSE

In case of HTTP response 200, you may ignore the response body.

Example request
Example response

Requesting an applicant check with different flow

If you plan to use this endpoint, please inform the Sum&Substance team, because this requires certain settings on our side.

You can programmatically request applicant check with flow that somehow differs from your usual checks.

POST /resources/applicants/{applicantId}/status/pending?reasonCode={reason}

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
reasonCode String Yes Code of the new flow

Provide a reasonCode to us so we could set up different flows on our side.

RESPONSE

In case of HTTP response 200, you may ignore the response body.

Example request
Example response

Getting document images

Since a user may re-upload images several times and, for example, also change a passport photo to an ID card, it might be tricky for you to understand which ones actually made the applicant pass or fail. Therefore it’s a two-step process.

If you are interested in receiving images that were part of the final verification, you should perform the following two steps. Please note that you should have special permissions to make this request (contact the Sum&Substance tech team).

First step:

GET /resources/applicants/{applicantId}/requiredIdDocsStatus

REQUEST HEADERS
Name Type Value
Accept String application/json; charset=UTF-8
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
RESPONSE

A breakdown for each step. Each step contains an array of images' IDs (could be several, e.g. if two sides were uploaded). You can get images with those IDs by issuing a request described below.

Example request:
Example response:

Second step:

GET /resources/inspections/{inspectionId}/resources/{imageId}

REQUEST ARGUMENTS
Name Type Required Description
inspectionId String Yes Inspection ID (it's a part of an applicant)
imageId String Yes Image ID (see above)
RESPONSE

Binary content representing a document. The Content-Type response header more precisely describes the response mime-type.

Example request:

Adding an applicant to blacklist

If for some reason you need to add an applicant to the blacklist, you can use this endpoint. It is necessary to add the reason for adding the applicant to the blacklist.

POST /resources/applicants/{applicantId}/blacklist

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
note String Yes Reason or note for the applicant added to the blacklist (URL encoded)
RESPONSE

The answer will contain the applicant added to the blacklist.

Example request
Example response

Sharing applicants between partner services

Sometimes our clients partner with each other and want to simplify and speed up verification for their users when they have passed KYC once via Sum&Substance.

Assume a user A passed KYC in service X, and is now registering at the partner service Y. If X agrees to share information about A with Y, it can be done as follows:

  • X generates a share token and passes it to Y
  • Y calls our API with the share token and receives an applicant for user A (with all its data and documents) on their account

Generating a share token

Example of getting a share token (done by X)
Example response

POST /resources/accessTokens/-/shareToken?applicantId={applicantId}&forClientId={clientId}

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID on service X
forClientId String Yes Client ID of service Y (ask them for this information)
ttlInSecs Integer No Time to live in seconds (default: 1200)
RESPONSE

Share token in the token field of the returned JSON (see example).

Importing an applicant

Example of importing an applicant (done by Y)
Example response

POST /resources/applicants/-/import?shareToken={shareToken}

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
shareToken String Yes Share token generated by X
RESPONSE

Applicant entity in service Y. Note a new applicant ID returned in the response. Bind it in your system as needed.

Please note that share tokens will be invalidated once used.

Resetting an applicant

In very rare cases, it is required to change the status of the applicant to init. For example, if a user has contacted support with a request to re-pass verification. Or if the applicant has been rejected with FINAL reviewRejectType and needs to be allowed to re-upload the documents.

If you plan to use this endpoint, please inform the Sum&Substance team, as we may be able to suggest another flow for your case.

POST /resources/applicants/{applicantId}/reset

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
applicantId String Yes Applicant ID
Example request
Example response

Payment Methods

Sum&Substance provides functionality for receiving documents related to payments and analyzes the information provided.

We currently work with the following types of documents:

  • Bank cards
  • Wire transfers
  • Crypto wallets
  • eWallets

You can see how to set up the necessary steps in our demo.

Adding payment method

Adding a payment method is done in 2 steps:

  • Adding information about the method
  • Adding an image (if necessary)

Adding information about the payment method

POST /resources/applicants/{applicantId}/paymentMethods

REQUEST HEADERS
Name Type Value
Authorization String OAuth2 token
Content-Type String application/json
REQUEST ARGUMENTS
Name Type Required Description
#{body} Hash Yes An object representing a payment method (see example)
body ATTRIBUTE
Name Type Required Description
type String Yes bankCard, eWallet, wireTransfer or cryptoWallet
subType String No VISA, MASTERCARD, BTC, ...
data String No An object representing a payment method to compare with data from the end user
RESPONSE
Name Type Required Description
id String Yes Unique identifier of the payment method
type String Yes bankCard, eWallet, wireTransfer or cryptoWallet
subType String No VISA, MASTERCARD, BTC, ...
Example request
Example response
{
  "id": "5d44a87a0a975a27bc5c9440",
  "type": "cryptoWallet",
  "subType": "BTC"
}

Adding an image

POST /resources/applicants/{applicantId}/paymentMethods/{paymentMethodId}/images

REQUEST HEADERS
Name Type Value
Authorization String OAuth2 token
Content-Type String multipart/form-data
FORM DATA
Name Type Required Description
content Binary Yes A photo of a document
RESPONSE

JSON representing added document information.

Example request

Requesting additional payment method

If the user has already uploaded a document and you want to ask them to upload another document, you need to perform such a request:

POST /resources/applicants/{applicantId}/paymentMethods

REQUEST HEADERS
Name Type Value
Authorization String OAuth2 token
Content-Type String application/json
REQUEST ARGUMENTS
Name Type Required Description
#{body} Hash Yes An object representing a payment method (see example)
body ATTRIBUTE
Name Type Required Description
type String Yes bankCard, eWallet, wireTransfer or cryptoWallet
subType String No VISA, MASTERCARD, BTC, ...
data String Yes An object representing a payment method. requiredIdDoc contains information for comparison with data from the end user.
RESPONSE

JSON representing added document information.

Name Description
number/address number/address of crypto wallet
checkType type of check
riskScore risk score of crypto wallet
status status of crypto wallet
Example request
If you want to add only the crypto-wallet for verification

Getting information about payment methods

Example response (you can get this info using method)
{
  "list": {
    "items": [
      {
        "id": "5be9a4710a975a0e0d822a00",
        "createdAt": "2018-11-12 16:04:01",
        "clientId": "client_id",
        "inspectionId": "5be9a4710a975a0e0d822a01",
        "jobId": "7497e63e-9206-4dcb-9741-2dfa705f8e70",
        "externalUserId": "external_user_id",
        "info": {
          "paymentInfo": {
            "reviewResult": null,
            "paymentMethods": [
              {
                "id": "5d498a490a975a0b873e6c77",
                "type": "cryptoWallet",
                "subType": "BTC",
                "createdAt": "2019-08-06 14:10:17",
                "deleted": null,
                "data": {
                  "number": "NUMBER"
                },
                "imageIds": null,
                "reviewResult": {
                  "reviewAnswer": "GREEN"
                },
                "checks": [
                  {
                    "id": "5d4acd400a975a0b8748deba",
                    "answer": "GREEN",
                    "checkType": "cryptoWalletRiskScore",
                    "inputData": {
                      "number": "NUMBER"
                    },
                    "cryptoWalletRiskScoreInfo": {
                      "riskScore": 0.182,
                      "addressSummaryData": {
                        "address": "NUMBER",
                        "balance": 643094,
                        "owner": null,
                        "received": 1610402,
                        "riskscore": {
                          "riskScoreEntitySignals": null,
                          "value": 0.182,
                          "updatedList": null
                        },
                        "status": "active",
                        "firstActivity": null,
                        "lastActivity": null,
                        "nTx": null,
                        "sharedBalance": null,
                        "sharedReceived": null
                      }
                    },
                    "createdAt": "2019-08-07 13:08:16"
                  }
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

Webhooks

The test server does not automatically check the applicants. If you need to change the status of the applicant or get webhook on the test server, you can do it by yourself.

We can send webhooks not only via HTTP request, but also as notifications in Slack, Telegram or via email.

Webhooks types

The types of webhooks we send depend on the settings.

Value Description
applicantReviewed When verification is completed. Contains the verification result. More information about this type of webhook can be found here.
applicantPending When a user uploaded all the required documents and the applicant's status changed to pending.
applicantCreated When an applicant is created.
applicantOnHold Processing of the applicant is paused for an agreed reason.
applicantPrechecked When primary data processing is completed.
Example applicantReviewed webhook payload you can find here. Example applicantCreated webhook payload:
Example applicantPending webhook payload:
Example applicantOnHold webhook payload:
Example applicantPrechecked webhook payload:

Testing on the test environment

On a test environment you can try receiving results via applicantReviewed webhooks by triggering the following endpoint (provide either GREEN or RED to simulate a positive or negative answer, correspondingly):

POST /resources/applicants/{applicantId}/status/testCompleted

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
Accept String application/json; charset=UTF-8
Content-Type String application/json
REQUEST ARGUMENTS
Name Type Required Description
#{body} Hash Yes Please see example
applicantId String Yes Applicant ID
REQUEST BODY
Name Type Required Description
reviewAnswer String Yes Provide GREEN or RED to simulate answer you need
rejectLabels String Yes Reasons for rejections
reviewRejectType String No Choose EXTERNAL, FINAL or RETRY to simulate other types of rejections
RESPONSE

In the case of HTTP response 200, you may ignore the response body.

Example request to change the applicant status to GREEN
Example request to change the applicant status to RED
Example response

Verifying webhook sender

In order to make sure that a webhook is sent by us, we have the possibility to sign it with the HMAC Sha1Hex algorithm. If you want to utilize this feature, we need to agree on a secret key that will be used for signing the HTTP webhook body. To compute digest correctly, you need to convert the HTTP webhook body into bytes and then perform calculations. The header x-payload-digest will be added to the webhook. In order to verify that a webhook is sent by us, you compute the same digest using the HTTP body and a secret key and compare it to the digest value that we've sent to you.

In order to check that you compute the digest the same way we do, you can call the following endpoint.

POST /resources/inspectionCallbacks/testDigest?secretKey={secretKey}

REQUEST HEADERS
Name Type Value
Authorization String Bearer token
REQUEST ARGUMENTS
Name Type Required Description
#{body} Hash Yes Any payload
secretKey String Yes A secret key that might be used for signing
RESPONSE
Value Description
digest Result of the calculation

Please test your webhook before sending its URL to us. At a minimum, it should not give a 500 HTTP response or require any sort of authorization.

Example request to get digest
Example response
Example request to client's endpoint
Example of computing digest on JS

Migration guide from old webhooks

There are some differences from the previous version.

  • New types of webhooks have been added.
  • Digest is passed as x-payload-digest header.
  • Keys like reviewAnswer, clientComment and others were in the entity named review, now this entity is called reviewResult.
  • You can configure which webhooks you want to receive in the dashboard.
Last Updated: 12/9/2019, 6:22:45 PM