Webhooks

TrustVault provides webhooks to alert you of the changes in that status of your transactions. These are POST requests delivered to your server and are sent as soon as an event occurs. The body of the request contains the details of the event.

Upon receiving a webhook notification, it should be acknowledged with a HTTP success response 20x. Otherwise the webhoook notification will be attempted to be sent again according to the following schedule:

  • 1 minute from the previous attempt
  • 2 minutes from the previous attempt
  • 15 minutes from the previous attempt
  • 2 hours from the previous attempt
  • 10 hours from the previous attempt
  • 24 hours from the previous attempt

Events

Event Description
Bitcoin Received A bitcoin wallet belonging to the trustId has received a transaction
Ethereum Received An ethereum wallet belonging to the trustId has received a ETH or ERC20 transaction
Bitcoin Transaction Created A bitcoin transaction has been created
Ethereum Transaction Created An ethereum transaction has been created
Policy Change Request Created There is a request to change the policy associated with a wallet

Event Object

Attribute Description
messageId string
Unique identifier for the webhook notification
version string
The webhook version
type string
The webhook notification type - BITCOIN_TRANSACTION_RECEIVED/ETHEREUM_TRANSACTION_RECEIVED/POLICY_CHANGE_REQUEST_CREATED
timestamp integer
The timestamp of the webhook notification
isoTimestamp string
The timestamp of the webhook notification in ISO 8601 format
payload object
The payload for the webhook notification

Bitcoin Received Event

The bitcoin received webhook will be triggered as soon as there is 1 confirmation from a miner.

Bitcoin Received Payload

Attribute Description
trustId string
Unique identifier for the TrustVault user
subWalletId object
Unique identifier for the wallet
subWalletIdString string
Unique identifier for the wallet, in string format
transactionAmount string
The transaction amount in satoshi (integer string)
bitcoinAddress string
The UTXO receive address of the transaction
transactionType string
The type of transaction - RECEIVED or SELF
blockHeight integer
The block number where the transaction was included in
transactionId string
Unique identifier for the transaction in the blockchain
transactionBlockTime integer
The timestamp of the block
transactionAmountInBtc string
The transaction amount in BTC (float string)

Ethereum Received Event

The ethereum received webhook will be triggered as soon as there is 1 confirmation from a miner.

Ether/ERC20 Transfer Received Payload

Attribute Description
trustId string
Unique identifier for the TrustVault user
subWalletId object
Unique identifier for the wallet
subWalletIdString string
Unique identifier for the wallet, in string format
from string
Ethereum address where the transaction was sent from
to string
Ethereum address where the transaction was sent to (*)
transactionValue string
Transaction value in Wei
transactionValueInEth string
Transaction value in Eth
transactionType string
The type of transaction - RECEIVED/SELF
tokensData array
Token data (see token structure below) (**)
transactionId string
Unique identifier for the transaction in the blockchain
blockHeight string
The block number where the transaction was included in
gasUsed string
Gas used by the transaction
gasPrice string
Gas price of the transaction
transactionBlockTime number
The timestamp of the block

(*)

  • In Ether transfers, to has the value of ethereum address where the Ether is transferred to.
  • In ERC20 transfers, to has the value of smart contract that manages the token transfer.

(**)

  • In Ether transfers, this array will be empty.

Ethereum Transaction Created Payload

Attribute Description
trustId string
Unique identifier for the TrustVault user
subWalletId object
Unique identifier for the wallet
subWalletIdString string
Unique identifier for the wallet, in string format
assetSymbol string
The symbol associated with the asset being transacted
chain string
The chain associated with this transaction. i..e ETHEREUM
signData object
The full chain specific data associated with this transaction
policyData object
The full wallet policy

Bitcoin Transaction Created Payload

Attribute Description
trustId string
Unique identifier for the TrustVault user
subWalletId object
Unique identifier for the wallet
subWalletIdString string
Unique identifier for the wallet, in string format
assetSymbol string
The symbol associated with the asset being transacted
chain string
The chain associated with this transaction. i..e BITCOIN
fee string
Fee in Satoshis for this transaction
signData object
The full chain specific data associated with this transaction
policyData object
The full wallet policy

Token Data

Attribute Description
to string
Ethereum address where the tokens were transferred to
quantity string
Quantity of tokens transferred
symbol string
Token symbol
assetName string
Name of the token
tokenDirectionType string
Direction of token transfer - RECEIVED/SELF

Policy Change Request Created

Attribute Description
trustId string
Unique identifier for the TrustVault user
requestId string
Unique identifier for the request
policyTemplate object
Object containing the new policy for the wallet
recovererTrustVaultSignature string
The r, s values of the signature of the SHA-256(DER(policy)) as signed by the TrustVault provenance key. This ensures that you can confirm that the policy has come from TrustVault
unverifiedDigestData string
The policy object containing properties for easy signing

UnverifiedDigestData

Called “unverified” because the onus is on the caller to ensure that the message contains the same data that has been requested for this wallet. i.e. if you requested a new policy you can construct the policy object you expect, DER encode it, SHA-256 hash it and then check that your digest matches the shaSignData. Additionally, you can verify the recovererTrustVaultSignature to ensure the data was signed by TrustVault.

For testing, verification is not required and you can simply sign the shaSignData or signData depending on if your signing code request the raw message or the digest (SHA-256).

The TrustVault node.js SDK will do verification of the payload for you

Attribute Description
signData string
The message containing the DER encoded policy that must be signed if you are in control of a publicKey referenced in the policy
shaSignData string
The SHA-256 digest of the message

PolicyTemplate

Attribute Description
delegateSchedules array
An array of delegate schedules which contain and array of clauses
recovererSchedules array
An array of delegate schedules which contain and array of clauses

Sub Wallet ID object

Attribute Description
id string
Unique identifier for the HD Wallet
type string
The chain identifier where the sub wallet belongs to (“BTC”, “BINANCE”)
index number
Index of the sub wallet in the HD Wallet starting at zero (integer)

Sample Bitcoin Received Event Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"messageId": "87f49826-dafb-46e9-a9bc-6ed7ef61f811",
"version": "1.0.2",
"type": "BITCOIN_TRANSACTION_RECEIVED",
"timestamp": 1588323320463,
"isoTimestamp": "2020-05-01T09:18:31.354Z",
"payload": {
"subWalletIdString": "f63b2ff1-f02b-48df-8b9f-bc57f5c57061/BTC/0",
"trustId": "f67ddcf6-e95d-4aa7-9a2d-e855ba5dc380",
"subWalletId": {
"id": "f63b2ff1-f02b-48df-8b9f-bc57f5c57061",
"type": "BTC",
"index": 0,
},
"transactionAmount": "142498030",
"bitcoinAddress": "342ftSRCvFHfCeFFBuz4xwbeqnDw6BGUey",
"transactionType": "RECEIVED",
"blockHeight": 627997,
"transactionId": "97f1f9150a992ac5309a0837ef3309757dc6359b8355867933d693b7c6a1ae98",
"transactionBlockTime": 1588078790,
"transactionAmountInBtc": "1.4249803",
}
}

Sample Bitcoin Transaction Created Event Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
{
"type": "BITCOIN_TRANSACTION_CREATED",
"messageId": "7bd6edad-e23f-476b-8cab-714cb80e4840",
"timestamp": 1588323320463,
"isoTimestamp": "2020-05-01T09:18:31.354Z",
"version": "1.0.0",
"payload": {
"requestId": "8f18b68b-eeb6-0b4c-9c93-2921b336f359",
"fee": 900,
"trustId": "c6b18fb4-3eac-420b-91f3-5ee1f679a16d",
"assetSymbol": "BTC",
"chain": "BITCOIN",
"policyData": {
"...<PolicyAssociatedWithWallet>"
},
"signData": {
"transaction": {
"version": 1,
"inputs": [
{
"address": "37thS2qbjZD47eT7M2txL1NxUywEq3hfye",
"txId": "4722c3f2d6a30fc481d237ccb4e6037cc02e3f4ff65e965b6cdbeaf0e59f52d6",
"outputIndex": 1,
"script": "a91444064f6cf221f0e64a0140b7e3707edd64bb6a1387",
"sequence": "ffffffff",
"value": 1642873750,
"publicKeyProvenanceData": {
"publicKey": "046386a33646742eeba5d202c09f123819b9785ddea5b78b5b64b36dd60397b36507a493d7a0a38334a6168f73e4ef9dfed1258b6248e8015c0e148b04cd3f6dc6",
"path": [
"0x80000049",
"0x80000000",
"0x80000000",
"0x1",
"0x45b"
],
"accountHSMProvenanceSignature": "0261a5cd98e8bef2f2610f8f09fa07d12731f69835daccccd12763dbc6137444d2cd5c850d18dc7768cfa5582f04367a58ecaeccbfde944cbbce42cf4884a024",
"unverifiedAddress": "37thS2qbjZD47eT7M2txL1NxUywEq3hfye",
"addressType": "COMPATIBILITY"
},
"unverifiedDigestData": {
"transactionDigest": "e928bcb8acf4bcda5c71151041c144e227ba60f30789fd470bd580391aa97d4a",
"signData": "30400420e928bcb8acf4bcda5c71151041c144e227ba60f30789fd470bd580391aa97d4a301c0205008000004902050080000000020500800000000201010202045b",
"shaSignData": "0ad7b306818ac11e812241fca40af3a621c348d25660fef9f5f59dacca14364f"
}
}
],
"outputs": [
{
"recipientAddress": "3AnVHpLRg63RT5Tg7XbUBtU9yFtJirT4RG",
"amountToSend": 9210000
},
{
"recipientAddress": "3MMUFRuHqjwrDbZGoq5yZgqfktD5gLGMKk",
"amountToSend": 1633653790,
"publicKeyProvenanceData": {
"publicKey": "040fbb85f7d9137ec3e88ddb88a79dbcc826e2de68fe4fb658bcef8031b1fd4b7507efc7ae6c014720929dfd5556756c387b9f7ce749ef277716d28d0ce973971a",
"path": [
"0x80000049",
"0x80000000",
"0x80000000",
"0x1",
"0x45d"
],
"accountHSMProvenanceSignature": "e1efc0d84050d61de14c584713d47147bbd6c93dedf56e55fc57e962751fccaebab89bade9b46a8cbbe7bfcaeee4d4ca981b87ff83e1dddda2f447f8809f2dbe",
"unverifiedAddress": "3MMUFRuHqjwrDbZGoq5yZgqfktD5gLGMKk",
"addressType": "COMPATIBILITY"
}
}
],
"lockTime": 0,
"sighash": 1
}
}
}
}

Sample Ethereum Transaction Created Event Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
"version": "1.0.0",
"type": "ETHEREUM_TRANSACTION_CREATED",
"messageId": "7bd6edad-e23f-476b-8cab-714cb80e4840",
"subWalletId": {
"id": "7bd6edad-e23f-476b-8cab-714cb80e4840",
"type": "ETH",
"index": 0
},
"subWalletIdString": "7bd6edad-e23f-476b-8cab-714cb80e4840/ETH/0",
"payload": {
"assetSymbol": "ETH",
"chain": "ETHEREUM",
"requestId": "020752e8-92bb-e79d-2348-753bb354b9af",
"trustId": "c6b18fb4-3eac-420b-91f3-5ee1f679a16d",
"signData": {
"transaction": {
"nonce": 134,
"gasPrice": "450000000000",
"gasLimit": "21000",
"chainId": 1,
"v": 1,
"to": "0x49eFc4E998996BE6611dE2415e8D82Ca4a21eCd7",
"fromAddress": "0x033c93eb0A03fbDbaa9f6aDAa0394D3b3321f09A",
"value": "161900000000000000",
"data": "0xded9382a0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000071517207d36db587000000000000000000000000000000000000000000000009fe67f723a43be0dc00000000000000000000000000000000000000000000000006ed058f927f79b500000000000000000000000049efc4e998996be6611de2415e8d82ca4a21ecd7000000000000000000000000000000000000000000000000000000005f883caf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c5d151702d3ca210e9dfdfc6ae62d10b3c712d09969783fdf7449e0888bd7b3412dac41121296b138921fece4a768451248298acbf988b97cbbe0c03dc400f0df"
},
"hdWalletPath": [
"0x80000044",
"0x80000060",
"0x80000000",
"0x0",
"0x0"
],
"unverifiedDigestData": {
"transactionDigest": "ed0c744d7a8ef7b675362be2d69f94aefc2ba357fc0ea3c6c172f5af864b4e62",
"signData": "303f0420ed0c744d7a8ef7b675362be2d69f94aefc2ba357fc0ea3c6c172f5af864b4e62301b020500800000440205008000006002050080000000020100020100",
"shaSignData": "bb810d73720a1f350f22c9873875ec0632377ff049cdb066c2b2e7f50af575ce"
}
},
"policyData": {
"...<PolicyAssociatedWithWallet>"
}
}
}

Sample Ether Received Event Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"version": "1.0.0",
"type": "ETHEREUM_TRANSACTION_RECEIVED",
"messageId": "4cfe6535-959b-4759-a9a2-043242e2ff57",
"timestamp": 1598363410185,
"isoTimestamp": "2020-08-25T13:50:10.185Z",
"payload": {
"trustId": "59ed6b8e-04f8-4815-ae59-f5e897ba3783",
"transactionValue": "2200000000000000",
"transactionValueInEth": "0.0022",
"transactionType": "RECEIVED",
"tokensData": [],
"transactionId": "0x657866589816879098d2c0644c832cb772f091929e714033e1123bdb35587a15",
"blockHeight": "8562864",
"from": "0xEfdF561eB7eB03BF1A2e52466F7A3628a9Bb94ec",
"to": "0xe191edfa3ea87452857e81ebc744d3d94d103bc0",
"gasUsed": "21000",
"gasPrice": "102000000000",
"subWalletId": {
"id": "d11710e5-9354-4a28-accd-7e45f77f2b83",
"type": "ETH",
"index": 0
},
"subWalletIdString": "d11710e5-9354-4a28-accd-7e45f77f2b83/ETH/0",
"transactionBlockTime": 1598363384
}
}

Sample ERC20 Received Event Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"version": "1.0.0",
"type": "ETHEREUM_TRANSACTION_RECEIVED",
"messageId": "e04d3c44-8ba2-4403-bc20-8564af6d3939",
"timestamp": 1598362511233,
"isoTimestamp": "2020-08-25T13:35:11.233Z",
"payload": {
"trustId": "59ed6b8e-04f8-4815-ae59-f5e897ba3783",
"transactionValue": "0",
"transactionValueInEth": "0",
"transactionType": "RECEIVED",
"tokensData": [
{
"to": "0xe191edfa3ea87452857e81ebc744d3d94d103bc0",
"quantity": "11.22",
"symbol": "USDT",
"assetName": "Tether USD",
"tokenDirectionType": "RECEIVED"
}
],
"transactionId": "0x953090cc45568e88963de8c394dee14ee152936c4216441380186e5d19f4e6cf",
"blockHeight": "8562794",
"from": "0x16d27cf6a6cfe5b0ede09e4148a4c5c082577b3e",
"to": "0x786bae3ca30e6a7019eb11abfa22d81f0febdce6",
"gasUsed": "41194",
"gasPrice": "100000000000",
"subWalletId": {
"id": "d11710e5-9354-4a28-accd-7e45f77f2b83",
"type": "ETH",
"index": 0
},
"subWalletIdString": "d11710e5-9354-4a28-accd-7e45f77f2b83/ETH/0",
"transactionBlockTime": 1598362450
}
}

Sample Policy Change request created

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
"version": "1.0.0",
"type": "POLICY_CHANGE_REQUEST_CREATED",
"messageId": "142dc928-c4b8-4362-92dd-a1d16123d14a",
"timestamp": 1598362511233,
"isoTimestamp": "2020-08-25T13:35:11.233Z",
"payload": {
"trustId": "h2209c26-f228-8524-afe5-1f4bbedd80e8",
"requestId": "e2209c26-f228-8524-afe5-1f4bbedd80e7",
"policyTemplate": {
"delegateSchedules": [
[
{
"quorumCount": 1,
"keys": [
"04a526f56356cbe2726b6315e1d04aa01d8b8729be597fb406abb959b9e27c380bdb3961c650413b6407facf96570545720d09ef45f3ee0722310289c370ca52e4"
]
}
]
],
"recovererSchedules": [
[
{
"quorumCount": 2,
"keys": [
"047c5e15091230f87a2e2e14d11dea53103aad06e4e271db282621ccc377fdf100c339557350839aa88a6809879c83e1c3f37fc6a42d5d6bbe10eeeca1e9fd81ac",
"04dd294cbbd831c977b47bae433a94d564b1dc446b2d7d305d263bd178c74b2dd41687b21db7f08c11b01bc090b1bdd80d94f7d3a54b4881e810e0a4153a187966",
"04aed00304191764c4ab31225881cd071af3f62d7f2480758e0a5f58ba8543e316553e3acc3c7c0d50f718c7f947da8908155808f72071168a514ffc869d9e0b50",
"04a875847170e3542b731f8cd21f24f61573cc5b3a6af60125eb42fe11d8816e20a6972f50b7938c76ffa5f633627e0988ae881849527684b59ae13181081ab802",
"042170e7a2ca8d94bb459909c79828305a6038dde10f25e5b00dac36bfda559d2027eff8df58ed8b611c493c641c97defd870c44f045ef7577990a8935276482e1"
]
}
],
[
{
"quorumCount": 1,
"keys": [
"047b49c8d0292dc426d2545ceb83795d94336599b8945d9d6a582a8c584b85e8c9ce665ee0917ca6e53ea7fa0028ed1e1efabe3f147e0e7062e0a09782adfced9d"
]
}
],
[
{
"quorumCount": 1,
"keys": [
"048aba94e2d1e56b0e34a77ea0691787ee0bfbc50f39bad959d4fa62b137c89a9744eeade7d6ce47968ee8f63f89b04805871d955308a3c134fbd70a29aaa22844"
]
}
]
],
"expiryTimestamp": 1598398081
},
"recovererTrustVaultSignature": "faabc9cb6960d4abb9d8fa8c4bacdd9d18e550fea4670290a8ac8770e5b9d7a952df54e39877d942a8900621cf85121e1f1eba2c45e7e0b5c30d9d0a0611ec15",
"walletId": "662dcbdf-d303-4369-878c-fccccefefd64",
"unverifiedDigestData": {
"signData": "3082024e02045f459e81304c304a30480201013043044104a526f56356cbe2726b6315e1d04aa01d8b8729be597fb406abb959b9e27c380bdb3961c650413b6407facf96570545720d09ef45f3ee0722310289c370ca52e4308201f63082015a308201560201023082014f0441047c5e15091230f87a2e2e14d11dea53103aad06e4e271db282621ccc377fdf100c339557350839aa88a6809879c83e1c3f37fc6a42d5d6bbe10eeeca1e9fd81ac044104dd294cbbd831c977b47bae433a94d564b1dc446b2d7d305d263bd178c74b2dd41687b21db7f08c11b01bc090b1bdd80d94f7d3a54b4881e810e0a4153a187966044104aed00304191764c4ab31225881cd071af3f62d7f2480758e0a5f58ba8543e316553e3acc3c7c0d50f718c7f947da8908155808f72071168a514ffc869d9e0b50044104a875847170e3542b731f8cd21f24f61573cc5b3a6af60125eb42fe11d8816e20a6972f50b7938c76ffa5f633627e0988ae881849527684b59ae13181081ab8020441042170e7a2ca8d94bb459909c79828305a6038dde10f25e5b00dac36bfda559d2027eff8df58ed8b611c493c641c97defd870c44f045ef7577990a8935276482e1304a304802010130430441047b49c8d0292dc426d2545ceb83795d94336599b8945d9d6a582a8c584b85e8c9ce665ee0917ca6e53ea7fa0028ed1e1efabe3f147e0e7062e0a09782adfced9d304a304802010130430441048aba94e2d1e56b0e34a77ea0691787ee0bfbc50f39bad959d4fa62b137c89a9744eeade7d6ce47968ee8f63f89b04805871d955308a3c134fbd70a29aaa22844",
"shaSignData": "4c73c97e194f01929e033ba196f234cf7a8539e6e6e0d5a753425e487bf8fc7a"
}
},
}

Webhook Security

Webhook notifications should be verified using the message hash signature in the header and the secret key to prevent attackers from imitating valid webhooks.

Each webhook is associated with a single secret key, which is given upon registration of the webhook. The secret key is used to generate an HMAC using SHA-256 hash algorithm.

Webhook notifications will have an X-Sha2-Signature header in the request, containing the HMAC. The secret key should be used to compute the message hash signature using the complete event object. You must ensure that the generated hash signature matches the X-Sha2-Signature header sent by TrustVault.

Verifying HMAC-256 signature

1
2
3
4
5
6
7
8
9
10
11
12
13
import * as crypto from "crypto";

// NOTE: Do not hard code your secret key. Store it somewhere safe
const secretKey = "SECRET_KEY";
const hmac = crypto.createHmac("SHA256", secretKey);

// Compute the hash from the stringified JSON request.body
const computedHashSignature = hmac.update(request.body).digest("hex");
const expectedHashSignature = request.headers["X-Sha2-Signature"];

if (computedHashSignature !== expectedHashSignature) {
throw new Error("Webhook hash signature mismatch");
}

Handle Duplicate Events

Webhook endpoints might occasionally receive the same event more than once. In particular, during chain re-organisation a webhook event could be re-sent for the exact same transaction. We advise you to guard against duplicated event receipts by making your event processing idempotent.