Initiator API 2 - One Step Travel Rule Verification
Where should I integrate?
To Use
This API provides a convenient way to initiate a request, making the process easier. As the Travel Rule initiator, you should decide the direction, which will be specified in the request parameter: verifyDirection.
- (After On-Chain - Direction Value: 1) Receiver to sender check: You, as the assets receiver/Beneficiary VASP, verify to the assets sender/Originator VASP. You need to provide the
txId. The target is to verify the originator person in IVMS.. - (Before On-Chain - Direction Value: 2) Sender to receiver check: You, as the assets sender/Originator VASP, verify the receiver/Beneficiary VASP. You need to provide an
address. The target is to verify the beneficiary person in IVMS.
This API will automatically perform the steps of the Initiator API verification process based on the verifyDirection decision.
If verifyDirection=2
The API will process these steps:
- Address Verification: Send address only to the counter-party VASP. If address does not exist, will not proceed.
- PII Verification: Send the encrypted PII payload to counter-party VASP.
If verifyDirection=1
The API will process these steps:
- TX ID Verification: Send Transaction ID and Beneficiary Address to counter-party VASP. They will use the beneficiary address to check who is the originator of the transaction. If TX ID does not exist, will not proceed.
- PII Verification: Send the encrypted PII payload to counter-party VASP.
*The disadvantage is that this function call cannot be retried. If you are concerned about the eventual consistency of the verification, you should not use this feature. *This API is a synchronous call API, and a request will receive a result (timeout is 5 seconds). *Different directions place IVMS structure into different keys. For example: After On-Chain should put your company info to BeneficiaryVASP and put your user to Beneficiary. Before On-Chain should put your company info to OriginatingVASP and put your user to Originator in the IVMS.
Invoke this API to initiate a Travel Rule request.
POST /api/verify/v2/one_step
For further details of the API, refer to the API documentation for /api/verify/v3/one_step.
Before On-Chain - One Step Travel Rule
Goal: Send Originator and Beneficiary PII to counter-party VASP
curl --silent --location --request POST "/api/verify/v3/one_step" \
--cert-type P12 --cert ./certificate.p12:'[MY_PASSWORD_OF_CERT]' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer [YOUR LOGIN TOKEN]" \
--header "Connection: keep-alive" \
--data-raw "{
\"requestId\": \"[REQUEST ID]\",
\"network\": \"[NETWORK NAME]\",
\"address\": \"[Beneficiary Address]\",
\"tag\": \"[TAG]\",
\"txId\": null, // Before on chain is null
\"verifyDirection\": 2, // 2: Sender-to-receiver (Before On Chain)
\"targetVaspCode\": \"[TARGET_VASP_CODE]\",
\"ticker\": \"[TICKER NAME]\",
\"amount\": \"[CRYPTO TRASNFER AMOUNT: 0, 0.3, 1200000]\",
\"fiatName\": \"[FIAT NAME: USD/USDT/EUR/GBP...]\",
\"fiatPrice\": \"[FIAT RATIO PRICE]\",
\"sourceVaspCode\": \"[IF YOU ON-BEHALF SOMEONE INIT THIS REQUEST, PLEASE FILL IT THEM ID (Normally you don't need it, leave it blank if you're not vendor company)]\",
\"expectVerifyFields\": [\"110026\",\"110025\",\"110045\"],
\"piiSecuredInfo\": {
\"initiatorKeyInfo\": {
\"publicKey\": \"[MY_PUBLIC_KEY]\"
},
\"piiSecretFormatType\": \"[TARGET_PII_SECRET_FORMAT_TYPE]\",
\"piiSpecVersion\": \"[TARGET_PII_SPEC_VERSION]\",
\"receiverKeyInfo\": {
\"publicKey\": \"[THEIR_PUBLIC_KEY]\"
},
\"secretAlgorithm\": \"[TARGET_ALGORITHM]\",
\"securedPayload\": \"[PII]\"
}
}"
Details of these parameters (same as After on chain):
verifyDirection: the verify direction of travel rule: 1: is after on chain when you the deposit side, 2: is before on chain when you the withdraw side.amount: the amount of crypto currency, for example:0.01(for BTC), should not be empty, at lease to be:0, (Reference: Longest AVAX should be decimal(28,18) / numeric(28,18))ticker: the name(symbol) of crypto currency, the list could refer to: List of tickernetwork: the network name or chain symbol, the list could refer to: List of networkaddress: the address of the beneficiarytag: beneficiary address tag (memo). the tag is optional, if the network chain requires the tag like (XRP, XLM, TRX), then this is necessary.piiSecuredInfo: the PII information to be sentpiiSecretFormatType: support forFULL_JSON_OBJECT_ENCRYPT,JSON_FIELD_ENCRYPTorJSON_FIELD_HASH, if you're not using any library provided by GTR, please consider to useFULL_JSON_OBJECT_ENCRYPTas default encryption method.initiatorKeyInfo: only bring your public key information here, do not bring any private key or sensitive information. according the algorithm to select corresponding public key.receiverKeyInfo: the counter-party VASP's public key that selected in the previous api:vaspDetail.secretAlgorithm: the encryption algorithm, support forED25519_CURVE25519and more, please refer to: Secret Algorithm in GTRpiiSpecVersion: the version of the PII specification, please refer to: List of PII Specification VersionsecretPayload: the encrypted PII information, the format is base64 encoded string. (If the library already encrypted, don't do it again), current max of length is 60000 bytes.
requestId: the unique identifier for the transaction request, should generate from your service, the format is recommend to be"[YOUR_EXCHANGE_NAME]-[UUID_v4]", and this requestId will also pass to the counter-party VASP's system as wellfiatName: the name of fiat currency, its ok to use different currency like: EUR/USD/HKD...etc, could also usingUSDTstable coin, whatever thefiatPriceoramountis zero or not, should set the field name default toUSD.fiatPrice: the price of the crypto currency in fiat currency, for example:10000(for 10000 USD), should not be empty, at lease to be:0txId: the transaction id of the crypto currency, In the before on chain, this should be null, In the after on chain, this should be necessary.exceptVerifyFields: the fields that you want to verify and get the result from response (or callback), the list of fields id can refer to: PII Verify Fields
About expectVerifyFields
You should always have to set the expectVerifyFields as your compliance requirement, as a Originator, if you want to verify the beneficiary's info like:
| Field Name | VerifyFields ID | Required | Optional |
|---|---|---|---|
| Beneficiary Legal Person Name | 111001 | Yes | No |
| Beneficiary Country of Registration | 111022 | Yes | No |
| Beneficiary Natural Person National ID | 110045 | No | Yes (OR Logic) |
Then you should put the expectVerifyFields like:
{
"expectVerifyFields": [
"111001", // Beneficiary Legal Person Name
"111022", // Beneficiary Country of Registration
"110045" // Beneficiary Natural Person National ID
]
}
And the response will tell you the result, please see the next section.
Response: About PII Verify Fields
You will get following response, additionally if you brings expectVerifyFields, GTR will give you each field matching results as below example, look at to verifyFields:
{
"data": {
"targetVaspCode": "[TARGET_VASP_CODE]",
"requestId": "[REQUEST ID]",
"verifyFields": [
{
"message": "matched",
"status": 1, // matched
"type": "111006" // Beneficiary Natural Person Name
},
{
"message": "date of birth mismatch",
"status": 2, // not matched
"type": "111025" // Beneficiary Natural Person Date of birth
},
{
"message": "not support",
"status": 3, // not Support
"type": "110029" // Beneficiary Natural Person Name
},
{
"message": "place of birth missing",
"status": 4, // info missing
"type": "100024" // Originaotr Natural Person place of birth
},
{
"message": "date of birth exists",
"status": 5, // info exists
"type": "100025" // Originaotr Natural Person Date of birth
}
],
"piiSecuredInfo": {
"initiatorKeyInfo": {
"publicKey": "[MY_PUBLIC_KEY]"
},
"piiSecretFormatType": "[TARGET_PII_SECRET_FORMAT_TYPE]",
"piiSpecVersion": "[TARGET_PII_SPEC_VERSION]",
"receiverKeyInfo": {
"publicKey": "[THEIR_PUBLIC_KEY]"
},
"secretAlgorithm": "[TARGET_ALGORITHM]",
"securedPayload": "[PII From Beneficiary]"
}
},
"verifyMessage": "Verification Success",
"verifyStatus": 100000,
"verifyStage": "PII_VERIFICATION"
}
Theres five status:
| Status Enum Name | Status Enum Value (Integer) | Description | When will display |
|---|---|---|---|
| SKIP | 0 | Skip verify (Treat as Skip) | exists in expectVerifyFields |
| MATCH / PASS | 1 | Full match Or Pass (Treat as Success) | exists in expectVerifyFields |
| MISMATCH | 2 | Not match (Treat as Failed) | exists in expectVerifyFields |
| NOT_SUPPORT | 3 | Not support (Treat as Skip) | exists in expectVerifyFields |
| INFO_MISSING | 4 | Info missing, It means: counter-party VASP cannot verify, but they want to receive the fields as non-empty data, and you didn't send (Treat as Warning, because the deposit side could reject or pending to collect more info) | if counter-party VASP replied |
| INFO_EXISTS | 5 | Received / Exists, It means: counter-party VASP cannot verify, but they want to receive the fields as non-empty data, and you did. (Treat as Success) | if counter-party VASP replied |
From 0, 1, 2, 3, the result comes out if you ask in the expectVerifyFields, and the 4, 5 will be force put into results if counter-party VASP replied these status.
The verifyStatus does not relay on the expectVerifyFields, it is the compliance requirement from counter-party side, you can ignore it and focus on the verifyFields in the response, we will discuss more to the process of error handling in bottom of this page.
When to Start Blockchain Transfer
You compare all the verifyFields in the response and only focus to your compliance requirement, if all matches then start it.
This is a table for the result of matching:
| Field Name | VerifyFields ID | Status | Required From Initiator (YOU) | Required From Receiver |
|---|---|---|---|---|
| Beneficiary Natural Person Name | 111006 | MATCHED: 1 | Yes | Yes |
| Beneficiary Natural Person Date of birth | 111025 | MISMATCH: 2 | Yes | No |
| Beneficiary Natural Person Name | 110029 | NOT_SUPPORT: 3 | No | No |
| Originaotr Natural Person place of birth | INFO_MISSING: 4 | 100024 | No | Yes |
| Originaotr Natural Person Date of birth | INFO_EXISTS: 5 | 100024 | No | Yes |
The table from the response of:
...
[
{
"message": "matched",
"status": 1, // matched
"type": "111006" // Beneficiary Natural Person Name
},
{
"message": "date of birth matched",
"status": 2, // not matched
"type": "111025" // Beneficiary Natural Person Date of birth
},
{
"message": "not support",
"status": 3, // NOT SUPPORT
"type": "110029" // Beneficiary Natural Person Name
},
{
"message": "place of birth info missing",
"status": 4, // info missing
"type": "100024" // Originaotr Natural Person place of birth
},
{
"message": "date of birth exists",
"status": 5, // info exists
"type": "100025" // Originaotr Natural Person Date of birth
}
]
...
As Originator, you only check all you required fields is matched or not, decide to make transfer or not.
const originatorRequiredFieldIds = ["111006", "111025"]
let decideToTransfer = true
for(let fieldId of originatorRequiredFieldIds) {
let found = false;
for(let fieldResult of response.verifyFields) {
if(fieldResult.type == fieldId) {
found = true
switch(fieldResult.status) {
case 2: // mismatch
decideToTransfer = false
break;
}
break;
}
}
// not found in field results
if(!found) {
decideToTransfer = false
}
}
After on chain - one step travel rule
Goal: Send Originator's PII
curl --silent --location --request POST "/api/verify/v3/one_step" \
--cert-type P12 --cert ./certificate.p12:'[MY_PASSWORD_OF_CERT]' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer [YOUR LOGIN TOKEN]" \
--header "Connection: keep-alive" \
--data-raw "{
\"requestId\": \"[REQUEST ID]\",
\"network\": \"[NETWORK NAME]\",
\"address\": \"[Beneficiary Address (TO ADDRESS): *Check note 1]\",
\"tag\": \"[TAG]\",
\"txId\": \"[REQUIRED]\", // After on chain is required
\"verifyDirection\": 1 // 1: Receiver-to-sender (After On Chain)
\"targetVaspCode\": \"[TARGET_VASP_CODE]\",
\"ticker\": \"[TICKER NAME]\",
\"amount\": \"[CRYPTO TRASNFER AMOUNT]\",
\"fiatName\": \"[FIAT NAME: USD/USDT/EUR/GBP...]\",
\"fiatPrice\": \"[FIAT RATIO PRICE]\",
\"sourceVaspCode\": \"[IF YOU ON-BEHALF SOMEONE INIT THIS REQUEST, PLEASE FILL IT THEM ID (Normally you don't need it, leave it blank if you're not vendor company)]\",
\"expectVerifyFields\": [\"100026\",\"100025\",\"100045\"],
\"piiSecuredInfo\": {
\"initiatorKeyInfo\": {
\"publicKey\": \"[MY_PUBLIC_KEY]\"
},
\"piiSecretFormatType\": \"[TARGET_PII_SECRET_FORMAT_TYPE]\",
\"piiSpecVersion\": \"[TARGET_PII_SPEC_VERSION]\",
\"receiverKeyInfo\": {
\"publicKey\": \"[THEIR_PUBLIC_KEY]\"
},
\"secretAlgorithm\": \"[TARGET_ALGORITHM]\",
\"securedPayload\": \"[PII]\"
}
}"
*Note 1: Yes this is your user's address (Beneficiary/ TO ADDRESS), you have to use beneficiary address is because originator will always be hot wallet address, its not able to find who send the assets.
TXID: 0x570ea1e8026c7defa2d00bfabba73a8d3d51b23
| From Address (Originator) | To Address (Beneficiary) |
|---|---|
0x123456 (Hot wallet) | 0x110026 (Specific User) |
0x123456 (Hot wallet) | 0x93a124 (Specific User) |
0x123456 (Hot wallet) | 0xf319e2 (Specific User) |
Response
You will get following response:
{
"data": {
"targetVaspCode": "[TARGET_VASP_CODE]",
"requestId": "[REQUEST ID]",
"verifyFields": [
{
"message": "matched",
"status": 1, // matched
"type": "101006" // Originator Natural Person Name
},
{
"message": "date of birth matched",
"status": 2, // not matched
"type": "101024" // Originator Natural Person Date of birth
}
],
"piiSecuredInfo": {
"initiatorKeyInfo": {
"publicKey": "[MY_PUBLIC_KEY]"
},
"piiSecretFormatType": "[TARGET_PII_SECRET_FORMAT_TYPE]",
"piiSpecVersion": "[TARGET_PII_SPEC_VERSION]",
"receiverKeyInfo": {
"publicKey": "[THEIR_PUBLIC_KEY]"
},
"secretAlgorithm": "[TARGET_ALGORITHM]",
"securedPayload": "[PII From Beneficiary]"
}
},
"verifyMessage": "Verification Success",
"verifyStatus": 100000,
"verifyStage": "PII_VERIFICATION"
}
Pending Flow
It could be possible when you initiate the one-step request and get the message immediately like the example below:
Response:
{
"verifyMessage": "Verification Success",
"verifyStatus": 100002,
"verifyStage": "ADDRESS_VERIFICATION"
}
The verifyStage status, please refer to the table:
| Scenario | Stage Sequence | Stage Name | Stage ID |
|---|---|---|---|
| Before On Chain | Stage 1 | Address Verification Pending | ADDRESS_VERIFICATION |
| Before On Chain | Stage 2 | PII Verification Pending | PII_VERIFICATION |
| After On Chain | Stage 1 | TX Verification Pending | TRANSACTION_VERIFICATION |
| After On Chain | Stage 2 | PII Verification Pending | PII_VERIFICATION |
State Machine:
| Scenario | Stage Sequence | Stage ID | Previous State |
|---|---|---|---|
| Before On Chain | Stage 1 | ADDRESS_VERIFICATION | In ADDRESS_VERIFICATION state means PII_VERIFICATION is not done yet |
| Before On Chain | Stage 2 | PII_VERIFICATION | In PII_VERIFICATION means ADDRESS_VERIFICATION has been done |
| After On Chain | Stage 1 | TRANSACTION_VERIFICATION | In TRANSACTION_VERIFICATION state means PII_VERIFICATION is not done yet |
| After On Chain | Stage 2 | PII_VERIFICATION | In PII_VERIFICATION means TRANSACTION_VERIFICATION has been done |
When you receive the pending state, which means you will get the result asyncourizonly with webhook callback, the exprie of pending state will be 72hours, and GTR will callback you as webhook to your service, please refer to Pending Flow Chapters.
*If 72hours expired, GTR will still callback you with expired status.
In this case, it is still possible to send the assets on chain, if you're compliance requirement is only to submit the PII and allow to skip the verify results, please follow the case below.
(Withdraw) Before On Chain Pending Flow
We will recommend don't send the trasnaction if ADDRESS_VERIFICATION stage in pending or failed, only if PII_VERIFICATION success or failed (depends if your compliance allow to make transfer without verification result) to send the assets.
(Deposit) After On Chain Pending Flow
We will recommed to have questionnaire and let the user select the brand to get vaspCode and do after-on-chain to verify the originator, it will stuck the deposit. If you don't want to stuck it, then another way is to let your user declare the questionnaire and save to your service.
Verify Status
Please note that the verifyStatus in the PII verification scenario will generally be represented by the following codes, and the codes can be conduct into the two different stage.
First stage: Address or TX ID Verification
- 100000: Success
- 100002: Pending (Please see chapter Pending Flow)
- 200001: Address Not Found
- 200007: TX ID Not Found
Second stage: PII Verification
- 100000: Success
- 100002: Pending (Please see chapter Pending Flow)
- 200003: Verify Failed
Other status codes such as:
- 100001: Common Fail
- 200002: Cannot Decrypt
- 200006: Cannot Encrypt
- 100006: GTR Server Failed
- 100007: Originator Server Failed
- 100008: Beneficiary Server Failed
and not above codes are considered abnormal.
*Most of the VASP will reply immediately, a few VASP will reply as pending.
*If you are initiating a before on-chain request, which is direction: 2 (Sender to receiver check), please proceed directly to the next section - Initiator API 6 to update the on-chain TX ID to GTR. Otherwise, if you are initiating an after on-chain request, which is direction: 1, you do not need to perform this action.
IMPORTANT
If you're using Before on chain travel rule, you should invoke this API before you send the assets on chain.
And also, in this case Before go on chain do not set the txId in this payload, please on chain first, and send the API that will introduce in the next page.