# KYT
# KYT API Reference
All API requests must be authenticated as described in this section.
Also, pay attention to the failed requests. Error descriptions can be found here.
# Process transaction for existing applicant
POST /resources/applicants/{applicantId}/kyt/txns/-/data
If you do not have an applicant to process transaction, you can call
POST /resources/applicants/-/kyt/txns/-/data?levelName={levelName}
This method should only be used where there is no existing applicant with the same externalUserId
that you intend to pass within an applicant
object of request body.
The new applicant will be created using provided levelName
and data from the applicant
object.
Make sure to send a unique externalUserId
and a proper fullName
within an applicant
object to us to create fresh applicant and process transaction.
# REQUEST ARGUMENTS
Name | Type | Required | Description |
---|---|---|---|
applicantId | String | Yes | Id of applicant that have been already processed by SumSub KYC. In case you don't have such an applicant, provide - . |
levelName | String | No | Level name to create a new applicant. |
#{body} | Object | Yes | An object representing transaction (see example). |
# REQUEST BODY
Name | Type | Required | Description |
---|---|---|---|
txnId | String | Yes | Transaction ID. |
txnDate | Date | No | Time when the transaction was initiated (format yyyy-MM-dd HH:mm:ss+XXXX , e.g. 2022-11-24 23:37:02+0000) |
info | Object | Yes | Transaction info. |
props | Object | No | Transaction properties. Map of strings - json of custom keys and string values. |
applicant | Object | Yes | Transaction participant data with mandatory applicantId (sender or remitter depending on info.direction ). |
counterparty | Object | Yes | Transaction participant data (sender or remitter depending on info.direction ). |
sourceKey | String | No | Source Key indication to separate accesses to transactions. |
# info
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
direction | String | Yes | out (applicant sends to counterparty) or in (counterparty sends to applicant). |
amount | Double | Yes | Amount of sent funds. |
currencyCode | String | Yes | Currency code (USD, GBP, BTC, etc). |
paymentDetails | String | No | Comment with payment details. |
# applicant
AND counterparty
ELEMENTS ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
externalUserId | String | Yes | Transaction participant externalUserId. |
fullName | String | Yes | Participant's full name. |
type | String | Yes | Participant entity type (company or individual ). |
address | Object | No | Participant's address. Address available elements are described here. |
institutionInfo | Object | No | Transaction institution info. |
paymentMethod | Object | No | Info about payment method. |
device | Object | No | Device info. |
# institutionInfo
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
code | String | No | Transaction institution code. |
name | String | No | Transaction institution name. |
address | Object | No | Transaction institution address. Available address elements are described here. |
internalId | String | No | VASP ID for counterparty transaction. If provided, we trust the exchange and use the expected VASP for transaction approval. |
# paymentMethod
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
type | String | No | Payment method type: card /account /crypto . |
fingerprint | String | No | Representation of the account id: account number, iban or DC hash for card , crypto wallet address for crypto type etc. |
issuingCountry | String | No | Payment issuing country code in Alpha-3 format. |
3dsUsed | Boolean | No | Indication of 3d secure auth being used. |
2faUsed | Boolean | No | Indication of 2fa being used. |
# device
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
userAgent | String | No | Device user agent. |
sessionId | String | No | Transaction session identifier. |
sessionAgeMs | Long | No | Session lifetime in milliseconds. |
acceptLang | String | No | From browser, e.g. en . |
platform | String | No | Device platform, e.g. Mobile Android . |
fingerprint | String | No | Device fingerprint. |
address | Object | No | Device address. Address available elements are described here. |
coords | Object | No | Coordinates. |
ipInfo | Object | No | Data about used IP address. |
# coords
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
lat | Double | No | Position's latitude in decimal degrees. |
lon | Double | No | Position's longitude in decimal degrees. |
accuracy | Double | No | Accuracy of the latitude and longitude properties, expressed in meters. |
# ipInfo
ELEMENT ATTRIBUTES
Name | Type | Required | Description |
---|---|---|---|
lat | Double | No | Position's latitude in decimal degrees. |
lon | Double | No | Position's longitude in decimal degrees. |
ip | String | No | IP address. |
countryCode3 | String | No | Country Alpha-3 code. |
asn | Integer | No | ASN. |
asnOrg | String | No | ASN organisation. |
riskyAsn | Boolean | No | ASN is risky or not. |
# Example request
curl -X POST \
'https://api.sumsub.com/resources/applicants/631f268442d8290001e1eee9/kyt/txns/-/data' \
-H 'Content-Type: application/json' \
-d '{
"txnId": "631f268442d8290001e1eee9_newTxn",
"applicant": {
"externalUserId": "uniqueRemitterId",
"address": {
"country": "DEU",
"street": "Chauseestr. 60",
"postCode": "101115",
"town": "Berlin"
},
"device": {
"ipInfo": {
"ip": "87.141.63.130"
}
},
"institutionInfo": {
"code": "DEUTDEDB101",
"name": "Deutsche Bank"
},
"paymentMethod": {
"type": "card",
"fingerprint": "eg_hash_of_credit_card_number",
"issuingCountry": "GBR"
}
},
"counterparty": {
"externalUserId": "uniqueBeneficiaryId",
"fullName": "John Smith",
"type": "individual",
"institutionInfo": {
"code": "CRESCHZZXXX",
"name": "Credit Swiss (Schweiz)"
}
},
"info": {
"direction": "out",
"amount": 101.42,
"currencyCode": "GBP",
"paymentDetails": "Birthday Present"
},
"props": {
"customProperty": "Custom value that can be used in rules"
}
}'
# RESPONSE ATTRIBUTES
Response contains the information from created transaction (all attributes) above, with data
object and additional objects that contain
current review and scoring info of transaction.
# RESPONSE ATTRIBUTES
Name | Type | Optional | Description |
---|---|---|---|
id | String | No | Transaction ID. |
applicantId | String | No | An applicant ID. |
score | Integer | Yes | Transaction review scoring. |
data | Object | No | Transaction data that was sent before. |
review | Object | No | Transaction review result. |
scoringResult | Object | Yes | Detailed info on transaction scoring. |
# review
ELEMENT ATTRIBUTES
Name | Type | Optional | Description |
---|---|---|---|
reviewStatus | String | No | Transaction status ( onHold , completed , init ). |
reviewResult | Object | Yes | Object representing transaction review result. |
reviewResult.reviewAnswer | String | Yes | GREEN (approved), RED (rejected). |
# scoringResult
ELEMENT ATTRIBUTES
Name | Type | Optional | Description |
---|---|---|---|
matchedRules | Array of Objects | Yes | List of matched rules. |
action | String | No | Result of all matched rules combined (score /onHold /reject ). |
# matchedRules
ELEMENTS ATTRIBUTES
Name | Type | Optional | Description |
---|---|---|---|
id | String | No | Rule ID. |
name | String | Yes | Rule name. |
title | String | Yes | Rule title. |
score | Integer | Yes | Amount that will be added to overall score if rule is matched. |
action | String | No | Result action of the matched rule (score /onHold /reject ). |
# Example response
{
"id": "638f1e3656df2d000138bff0",
"applicantId": "631f268442d8290001e1eee9",
"data": {
"txnId": "631f268442d8290001e1eee9_newTxn",
"applicant": {
"address": {
"country": "DEU",
"street": "Chauseestr. 60",
"postCode": "101115",
"town": "Berlin"
},
"device": {
"ipInfo": {
"ip": "87.141.63.130"
}
},
"institutionInfo": {
"code": "DEUTDEDB101",
"name": "Deutsche Bank"
}
},
"counterparty": {
"fullName": "John Smith",
"type": "individual",
"institutionInfo": {
"code": "CRESCHZZXXX",
"name": "Credit Swiss (Schweiz)"
}
},
"info": {
"direction": "out",
"amount": 10100.42,
"currencyCode": "GBP",
"paymentDetails": "Birthday Present"
},
"props": {
"customProperty": "Custom value that can be used in rules"
}
}
"review": {
"reviewStatus": "onHold"
},
"scoringResult": {
"matchedRules": [
{
"id": "6391b0d1ac5401000190cf02",
"name": "PAAM2",
"title": "Large amount",
"score": 30,
"action": "onHold"
},
{
"id": "6391b0b2d8f0730001bea772",
"name": "PATX15",
"title": "Average amount for the same beneficiary for the last month is greater by 35% of average amount for last 3 months",
"score": 5,
"action": "score"
}
],
"action": "onHold"
}
}
# Getting transaction
GET /resources/kyt/txns/{id}/one
# REQUEST ARGUMENTS
Name | Type | Required | Description |
---|---|---|---|
id | String | Yes | Transaction ID. |
# RESPONSE ATTRIBUTES
Response contains the info from created transaction (all attributes) above with additional objects that contain current review and scoring info of requested transaction.
# Example response
{
"id": "638f1e3656df2d000138bff0",
"applicantId": "631f268442d8290001e1eee9",
"data": {
"txnId": "631f268442d8290001e1eee9_newTxn",
"applicant": {
"address": {
"country": "DEU",
"street": "Chauseestr. 60",
"postCode": "101115",
"town": "Berlin"
},
"device": {
"ipInfo": {
"ip": "87.141.63.130"
}
},
"institutionInfo": {
"code": "DEUTDEDB101",
"name": "Deutsche Bank"
}
},
"counterparty": {
"fullName": "John Smith",
"type": "individual",
"institutionInfo": {
"code": "CRESCHZZXXX",
"name": "Credit Swiss (Schweiz)"
}
},
"info": {
"direction": "out",
"amount": 101.42,
"currencyCode": "GBP",
"paymentDetails": "Birthday Present"
},
"props": {
"customProperty": "Custom value that can be used in rules"
}
}
"review": {
"reviewStatus": "onHold"
}
}
# Bulk transaction import
This endpoint allows you to import a list of transactions to your dashboards for our service to consider them in
the next transactions processing for scoring.
It expects transactions to be passed in valid NDJSON with the Content-Type: application/x-ndjson
header being set.
There is a limit on a number of transaction in the bulk import: 10000 records.
POST /resources/kyt/misc/txns/import
# REQUEST BODY
The list of JSON strings representing a KYT transaction divided by \n
is expected.
Transaction object contains properties below:
Name | Type | Required | Description |
---|---|---|---|
applicantId | String | Yes | Id of applicant that have been already processed by SumSub KYC for binding transaction to it. |
data | Object | Yes | Object representing transaction. Properties for it are shown above. |
# Example request
curl -X POST \
'https://api.sumsub.com/resources/kyt/misc/txns/import' \
-H 'Content-Type: application/x-ndjson' \
-d $'{
"applicantId": "636cee6b17d6c7000144673b",
"data": {
"txnId": "631f268442d8290001e1eee8_newTxn",
"applicant": {
"externalUserId": "uniqueRemitterId",
"address": {
"country": "DEU",
"street": "Chauseestr. 60",
"postCode": "101115",
"town": "Berlin"
},
"device": {
"ipInfo": {
"ip": "87.141.63.130"
}
},
"institutionInfo": {
"code": "DEUTDEDB101",
"name": "Deutsche Bank"
}
},
"counterparty": {
"externalUserId": "uniqueBeneficiaryId",
"fullName": "John Smith",
"type": "individual",
"institutionInfo": {
"code": "CRESCHZZXXX",
"name": "Credit Swiss (Schweiz)"
}
},
"info": {
"direction": "in",
"amount": 101.42,
"currencyCode": "GBP",
"paymentDetails": "Birthday Present"
}
}
}\n{
"applicantId": "636cee6b17d6c7000144673b",
"data": {
"txnId": "631f268442d8290001e1eee9_newTxn",
"applicant": {
"externalUserId": "uniqueRemitterId",
"address": {
"country": "DEU",
"street": "Chauseestr. 60",
"postCode": "101115",
"town": "Berlin"
},
"institutionInfo": {
"code": "DEUTDEDB101",
"name": "Deutsche Bank"
}
},
"counterparty": {
"externalUserId": "uniqueBeneficiaryId",
"fullName": "John Smith",
"type": "individual"
},
"info": {
"direction": "out",
"amount": 101.42,
"currencyCode": "GBP",
"paymentDetails": "Birthday Present"
}
}
}'
# RESPONSE ATTRIBUTES
Response contains a number of imported transactions.
# Example response
{
"createdCnt": 2
}
# Webhooks
The types of webhooks we send depend on the settings of the dashboard.
On the KYT request stats change, we send different types of webhooks depending on the transaction status.
Value | Description |
---|---|
applicantKytTxnApproved | Transaction was approved. |
applicantKytTxnRejected | Transaction was rejected. |
applicantKytOnHold | Transaction status was set to onHold . |
# Example applicantKytTxnApproved webhook payload
{
"applicantId": "634829375766b80001a40152",
"applicantType": "individual",
"correlationId": "f24f6616020245053139a6537303a251",
"sandboxMode": false,
"externalUserId": "customExternalUserId",
"type": "applicantKytTxnApproved",
"reviewResult": {
"reviewAnswer": "GREEN"
},
"reviewStatus": "completed",
"createdAtMs": "2022-10-24 12:42:07.143",
"clientId": "rda_for_test",
"kytTxnId": "j4rmpqoz7anug324n6tjho"
}
# Example applicantKytOnHold webhook payload
{
"applicantId": "634829375766b80001a40152",
"applicantType": "individual",
"correlationId": "98d4dac61c977c1b3f81d6ab78d29c3c",
"sandboxMode": false,
"externalUserId": "customExternalUserId",
"type": "applicantKytOnHold",
"reviewStatus": "onHold",
"createdAtMs": "2022-10-24 12:44:32.341",
"clientId": "rda_for_test",
"kytTxnId": "j8bqz29yn491vksi9qfydw"
}
# Example applicantKytTxnRejected webhook payload
{
"applicantId": "634829375766b80001a40152",
"applicantType": "individual",
"correlationId": "0f5a7c828bab750775564534fc0470a8",
"sandboxMode": false,
"externalUserId": "customExternalUserId",
"type": "applicantKytTxnRejected",
"reviewResult": {
"reviewAnswer": "RED",
"reviewRejectType": "FINAL"
},
"reviewStatus": "completed",
"createdAtMs": "2022-10-24 12:45:04.982",
"clientId": "rda_for_test",
"kytTxnId": "j8bqz29yn491vksi9qfydw"
}
# FAQ
# What should we do if we don't have a checked applicant to add a transaction to it?
If you'd like to create a transaction for a user that hasn't passed verification via our service yet, you can create a new applicant and add a transaction to it using single request POST /resources/applicants/-/kyt/txns/-/data?levelName={levelName}
.
More info is in documentation above.
# What happens to a transaction when it is added?
It is saved and then scored. Depending on the matched rules, the transaction may end up in the “approved”, “rejected” or “on hold” state. Also, it may affect an applicant by requesting additional verifications.
# How can I apply different rules to transactions from different sources?
Provide the sourceKey
field in the transaction data when creating it. Rules without a source key, or source keys
that include the transaction source key will be applied.
# How can I get notified about the scoring result?
You can set up webhooks the same way you do for the applicants. You can react on approved, rejected, and “on hold” transactions.
# How can I test a new rule without fear of breaking anything?
When installing a rule, it is in the Dry run mode by default, meaning that it will be applied but will not affect the final scoring. Additionally, on the Rules tab, you can test the rule by applying it to existing transactions retrospectively.
# What do I do with transactions that are put on hold?
You can explore them at My queues tab and make a final decision. Or you may decide to request an additional applicant action that you set up in advance, e.g. a liveness check, source of funds request, or a questionnaire. So when the applicant action is complete, the status of the transaction will change accordingly. Thus, you can automate risky or suspicious transactions.
# What if I want to have rules that are based on some custom data that are not in your data model?
Add custom fields into the props
map in the transaction data. We will be able to set up rules based on them.
# What can I base my rules on?
Pretty much on everything that we collected about the applicant or transaction. For example, applicant data, id docs, questionnaires, aggregations of data based on devices, geolocation, transaction amounts, and so on. If you miss anything, get in touch. In most cases, it would be an easy exercise to add it for you.
# Can I control certain transactions that match certain rules?
You can set up a custom queue based on rules and computed transaction status. Then you can review transactions that appear in such custom queues, take immediate actions, assign them to yourself or other colleagues for further investigation or confirm the resulting status (if it was approved or rejected).
# What do I need to pass to stay protected?
In general, as much information as you can map to our data structure, plus put the rest into custom properties if you think this field is important. Even if you do not have rules that work on certain fields, you may have some in the future. Then you can do a re-scoring in case you decide to add more rules.
# What kind of reporting do you have?
You can get the events that happened to a transaction, plus you can get the transactions in the CSV report or a detailed PDF report about a particular transaction. If you miss some reporting feature, contact us, and we will try to satisfy your request.
# How do I get reports on certain users or transactions of interest?
Set up a filter that matches your criteria and get a CSV export. If you miss some filters contact us, we will add them for you.
# Travel Rule
This appendix is designed to clarify all the matters for the travelRule integration in SumSub system using our KYT. The Travel Rule itself is a recomendation that mandates transfer and check of the personal data between VASP's that conduct cryptocurrency transaction. It has several steps which are covered in this document.
# Definitions
Further we will use this definitions:
- VASP - Virtual Asset Service Provider, e.g. an entity that uses cryptocurrency and is subjected to FATF regulation
- Personal Data - Any information about the user, included but not limited to full name and crypto account address
- Originator VASP - for the crypto transfer, the VASP that originates the transaction
- Sending user - for the crypto transfer, user on behalf of which originator VASP sends the transaction
- Beneficiary VASP - the VASP that receives the transaction
- Receiving user - the user to which account in beneficiary VASP the funds would be received
- Travel address - substitute for crypto address, which should be used to ensure data transfer via TRP protocol.
# General scenario for transactions
General scenario for all Travel Rule transaction is illustrated in a scheme below. Here is the detailed description of all steps:
- At the moment client need to start transaction, he sends /kyt/txns/-/data request to create a transaction. It should have all required fields, which can differ one case from another.
- After sending transaction, Sumsub checks that all required fields are present, and makes basic AML checks. If everything is fine, you will receive applicantKytOnHold webhook
- For incoming transactions, you will need to request additional information from Sumsub by sending GET /kyt/txn/id/one
- Sumsub will respond with full information about transaction
- After finalization of Travel Rule transaction, Sumsub will send applicantKytTxnApproved or applicantKytTxnDeclined webhook to you. If successfull, the Travel Rule transaction is finalized and financial transaction is expected.
# Getting a list of VASPs
GET /resources/vasps/-;?limit=10&offset=0&order=-createdAt
# REQUEST ARGUMENTS
Name | Type | Required | Description | Default |
---|---|---|---|---|
limit | Integer | No | Limit of applicant actions to be returned. | 10 |
offset | Integer | No | Offset of applicant actions to be returned. | 0 |
# Example request
curl -X POST \
'https://api.sumsub.com/resources/vasps/-;?limit=10&offset=0&order=-createdAt'
# RESPONSE ATTRIBUTES
Response contains a list of VASP objects
# Example response
{
"list" : {
"items" : [ {
"id" : "645e18bc5e7cbb71d51b2b51",
"createdAt" : "2023-05-12 10:45:16+0000",
"name" : "123",
"legalName" : "123",
"companyNumber" : "123",
"underRegulation" : false,
"headquartersCountry" : "GBR",
"url" : "111.com",
"email" : "[email protected]",
"verificationId" : "645e1bfdb0823d461d49dde7",
"vaspInfos" : [ {
"id" : "0823010c-6ef1-4825-b913-96ab067b7800",
"source" : "crystal",
"externalId" : "2133",
"name" : "Satoshi Nakamoto"
} ]
}, {
"id" : "645e16e65e7cbb71d51b2add",
"createdAt" : "2023-05-12 10:37:26+0000",
"url" : "sumsub.com",
"email" : "[email protected]",
"verificationId" : "645e171b5e7cbb71d51b2ae5",
"clientId" : "vasp_123123binance",
"vaspInfos" : [ {
"id" : "ea45fc57-d526-4b2c-8baa-609227f10f64",
"source" : "coinfirm",
"externalId" : "382",
"name" : "123123B"
}, {
"id" : "a0795908-49b9-4ed5-b72d-2cddc529b88d",
"source" : "crystal",
"externalId" : "1457",
"name" : "B"
} ]
} ],
"totalItems" : 2
}
}