Payout object
Deposit object example
Copy {
"type" : "payout" ,
"id" : "1815" ,
"attributes" : {
"amount" : "1.00000000" ,
"address" : "bcrt1qs8vkvgj53tdzxaxzcxrwnvtp0ezse0vmrjty6u" ,
"target_deposit" : "15987fbe-993e-45a8-93fa-cdd1a626488f" ,
"target_wallet" : null ,
"tracking_id" : "" ,
"label" : "" ,
"confirmations_needed" : null ,
"fee_amount" : "0.00000000" ,
"is_fee_included" : false ,
"status" : 2 ,
"rate_requested" : "1.00000000" ,
"callback_url" : "" ,
"force_blockchain" : false ,
"exp" : 8 ,
"tag_type" : null ,
"tag" : null ,
"destination" : {
"address_type" : "bech32" ,
"address" : "bcrt1qs8vkvgj53tdzxaxzcxrwnvtp0ezse0vmrjty6u"
} ,
"travel_rule_info" : {
"beneficiary" : {
"beneficiaryPersons" : [
{
"naturalPerson" : {
"name" : [
{
"nameIdentifier" : [
{
"primaryIdentifier" : "John" ,
"secondaryIdentifier" : "Smith"
}
]
}
] ,
"geographicAddress" : [
{
"country" : "Country" ,
"addressLine" : [
"Address"
] ,
"addressType" : "HOME"
}
]
}
}
]
}
}
} ,
"relationships" : {
"currency" : {
"data" : {
"type" : "currency" ,
"id" : "1000"
}
} ,
"wallet" : {
"data" : {
"type" : "wallet" ,
"id" : "3614"
}
}
}
}
Get payout
Request
GET
[base]/payout/
{id}
Filtering by object parameters can be applied according to the JSON API Specification .
Request example
cURL Python PHP
Copy curl --request GET \
--url [base]/payout/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/vnd.api+json'
Copy import requests
url = '[base]/payout/'
headers = {
'Authorization' : 'Bearer <token>' ,
'Content-Type' : 'application/vnd.api+json' ,
}
requests . get (url, headers = headers)
Copy <? php
use GuzzleHttp \ Client ;
use GuzzleHttp \ Exception \ RequestException ;
$client = new GuzzleHttp \ Client ();
try {
$res = $client -> get ( '[base]/payout/' , [
'headers' => [
'Authorization' => 'Bearer <token>' ,
'Content-Type' => 'application/vnd.api+json' ,
] ,
] ) ;
echo $res -> getBody () ;
} catch ( RequestException $e) {}
Response
In case of success, the response body contains a payout object or an array of objects (if the id
wasn’t specified).
The wallets list is paginated and the default page size is 10. You can adjust pagination according to the JSON API Specification .
Response codes
Create payout
Before creating a payout, you can retrieve parameters of the fields you need to fill in by calling the Payout options method.
For security reasons, an additional HTTP header with a unique idempotency key must be sent in the request. The key is a UUID4 string with hyphens, for example: 2dbcb513-bd35-404f-9709-e34878def180
.
Refer to Useful links for recommended programming packages and libraries that you can use to generate UUID4.
Request
POST
[base]/payout/
Request example
cURL Python PHP
Copy curl --request POST \
--url [base]/payout/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/vnd.api+json' \
--header 'idempotency-key: b6891f5b-d16f-4ba9-a1c4-9828be64a492' \
--data '{
"data": {
"type": "payout",
"attributes": {
"label": "My Payout",
"amount": "0.05",
"fee_amount": "0.00000550",
"address": "bcrt1q92k5z02dyrjahm4hput42nps3t7ryxzzz0vl76",
"tracking_id": "f12",
"confirmations_needed": 2,
"callback_url": "https://my.client.com/cb/",
"travel_rule_info": {
"beneficiary": {
"beneficiaryPersons": [
{
"naturalPerson": {
"name": [
{
"nameIdentifier": [
{
"primaryIdentifier": "John",
"secondaryIdentifier": "Smith"
}
]
}
],
"geographicAddress": [
{
"country": "Country",
"addressLine": [
"Address"
],
"addressType": "HOME"
}
]
}
}
]
}
}
},
"relationships": {
"wallet": {
"data": {
"type": "wallet",
"id": "1"
}
},
"currency": {
"data": {
"type": "currency",
"id": "1000"
}
}
}
}
}'
Copy import requests
import json
url = "[base]/payout/"
payload = json . dumps ({
"data" : {
"type" : "payout" ,
"attributes" : {
"label" : "My Payout" ,
"amount" : "0.05" ,
"fee_amount" : "0.00000550" ,
"address" : "bcrt1q92k5z02dyrjahm4hput42nps3t7ryxzzz0vl76" ,
"tracking_id" : "f12" ,
"confirmations_needed" : 2 ,
"callback_url" : "https://my.client.com/cb/" ,
"travel_rule_info" : {
"beneficiary" : {
"beneficiaryPersons" : [
{
"naturalPerson" : {
"name" : [
{
"nameIdentifier" : [
{
"primaryIdentifier" : "John" ,
"secondaryIdentifier" : "Smith"
}
]
}
],
"geographicAddress" : [
{
"country" : "Country" ,
"addressLine" : [
"Address"
],
"addressType" : "HOME"
}
]
}
}
]
}
}
},
"relationships" : {
"wallet" : {
"data" : {
"type" : "wallet" ,
"id" : "1"
}
},
"currency" : {
"data" : {
"type" : "currency" ,
"id" : "1000"
}
}
}
}
})
headers = {
'Content-Type' : 'application/vnd.api+json' ,
'Authorization' : 'Bearer <token>'
}
response = requests . request ( "POST" , url, headers = headers, data = payload)
print (response.text)
Copy <? php
$client = new Client ();
$headers = [
'Content-Type' => 'application/vnd.api+json' ,
'Authorization' => 'Bearer <token>'
];
$body = '{
"data": {
"type": "payout",
"attributes": {
"label": "My Payout",
"amount": "0.05",
"fee_amount": "0.00000550",
"address": "bcrt1q92k5z02dyrjahm4hput42nps3t7ryxzzz0vl76",
"tracking_id": "f12",
"confirmations_needed": 2,
"callback_url": "https://my.client.com/cb/",
"travel_rule_info": {
"beneficiary": {
"beneficiaryPersons": [
{
"naturalPerson": {
"name": [
{
"nameIdentifier": [
{
"primaryIdentifier": "John",
"secondaryIdentifier": "Smith"
}
]
}
],
"geographicAddress": [
{
"country": "Country",
"addressLine": [
"Address"
],
"addressType": "HOME"
}
]
}
}
]
}
}
},
"relationships": {
"wallet": {
"data": {
"type": "wallet",
"id": "1"
}
},
"currency": {
"data": {
"type": "currency",
"id": "1000"
}
}
}
}
}' ;
$request = new Request ( 'POST' , '[base]/payout/' , $headers , $body);
$res = $client -> sendAsync ( $request ) -> wait () ;
echo $res -> getBody () ;
Response
In case of success, the response body contains a newly created payout object .
Response codes
Travel rule
The travel_rule_info
object contains information about a payment receiver and must be filled in when creating a payout.
The object fields are different for natural and legal persons.
Natural person
Copy "travel_rule_info" : {
"beneficiary" : {
"beneficiaryPersons" : [
{
"naturalPerson" : {
"name" : [
{
"nameIdentifier" : [
{
"primaryIdentifier" : "First Name" ,
"secondaryIdentifier" : "Last Name"
}
]
}
] ,
"geographicAddress" : [
{
"country" : "Country" ,
"addressLine" : [
"Address"
] ,
"addressType" : "HOME"
}
]
}
}
]
}
}
The object contains the following key fields:
Legal person
Copy "travel_rule_info" : {
"beneficiary" : {
"beneficiaryPersons" : [
{
"legalPerson" : {
"name" : {
"nameIdentifier" : [
{
"legalPersonName" : "Name" ,
"legalPersonNameIdentifierType" : "LEGL"
}
]
} ,
"geographicAddress" : [
{
"country" : "Country" ,
"addressLine" : [
"Address"
] ,
"addressType" : "BIZZ"
}
]
}
}
]
}
}
The object contains the following key fields:
Precalculate fee
Use this method to precalculate possible blockchain fee options.
For calculations, you need to provide the identifier of a wallet from which the payout is made along with the destination address and payout amount.
Request
POST
[base]/payout/calculate/
Request example
cURL Python PHP
Copy curl --request POST \
--url /payout/calculate/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/vnd.api+json' \
--data '{
"data": {
"type": "payout-calculation",
"attributes": {
"amount": "0.0000001",
"to_address": "2N3Ac2cZzRVoqfJGu1bFaAebq3izTgr1WLv"
},
"relationships": {
"wallet": {
"data": {
"type": "wallet",
"id": "13"
}
},
"currency": {
"data": {
"type": "currency",
"id": "1000"
}
}
}
}
}'
Copy import requests
url = '[base]/payout/calculate/'
headers = {
'Authorization' : 'Bearer <token>' ,
'Content-Type' : 'application/vnd.api+json' ,
}
data = {
'data' : {
'type' : 'payout-calculation' ,
'attributes' : {
'amount' : '0.0000001' ,
'to_address' : '2N3Ac2cZzRVoqfJGu1bFaAebq3izTgr1WLv' ,
},
'relationships' : {
'wallet' : {
'data' : {
'type' : 'wallet' ,
'id' : '13' ,
}
},
'currency' : {
'data' : {
'type' : 'currency' ,
'id' : '1000' ,
},
},
},
},
}
requests . post (url, headers = headers, json = data)
Copy <? php
use GuzzleHttp \ Client ;
use GuzzleHttp \ Exception \ RequestException ;
$client = new GuzzleHttp \ Client ();
try {
$res = $client -> post ( '[base]/payout/calculate/' , [
'json' => [
'data' => [
'type' => 'payout-calculation' ,
'attributes' => [
'amount' => '0.05' ,
'to_address' => 'bcrt1q92k5z02dyrjahm4hput42nps3t7ryxzzz0vl76' ,
] ,
'relationships' => [
'wallet' => [
'data' => [
'type' => 'wallet' ,
'id' => '1' ,
] ,
] ,
'currency' => [
'data' => [
'type' => 'currency' ,
'id' => '1000' ,
] ,
] ,
] ,
] ,
] ,
'headers' => [
'Authorization' => 'Bearer <token>' ,
'Content-Type' => 'application/vnd.api+json' ,
] ,
] ) ;
echo $res -> getBody () ;
} catch ( RequestException $e) {}
Response
In case of success, the response body contains low, medium, and high blockchain fee values.
Response body example
Copy {
"data" : {
"type" : "payout-calculation" ,
"id" : "0" ,
"attributes" : {
"is_internal" : true ,
"fee" : {
"low" : "0.0001000" ,
"medium" : "0.0001000" ,
"high" : "0.1073686" ,
"dust_amount" : "0.0000000" ,
"warning" : null ,
"currency" : 1021
} ,
"commission" : {
"amount" : "0.0000000" ,
"currency" : 1021
}
}
}
}
Response codes
Payout callback
A callback is a notification sent to a user’s callback URL when a new payout-related transaction occurs in the blockchain. You can use the callback to make changes in your system and notify your payers.
Default callback
By default, the callback is sent after the transaction is confirmed. To get confirmed, a transaction should receive a necessary number of confirmation blocks and pass the AML verification. The required number of confirmations is determined by the confirmation_blocks
field in the Currency object . For example, the confirmation_blocks
field is set to 3
. This means that the callback will be sent after receiving three confirmations and passing the AML check.
Additional callback
It’s possible to trigger an additional callback. To do this, specify a new number in the confirmations_needed
field when calling the Create payout method. For example, the confirmation_blocks
value is set to 3
and the confirmations_needed
value is set to 1
. In this case, two callbacks will be sent: one (additional) after a transaction receives one confirmation and another one (default) after a transaction receives three confirmations.
Callback processing
Upon receiving a required number of confirmations related to a new transaction, the callback is sent to your server if the payout request body includes a valid callback URL. The callback is sent by a POST
-request, which contains useful JSON payload.
To verify that the callback was sent by B2BINPAY, generate an HMAC-signature using sha256
as algorithm: sha256
hash of the concatenation of your login and password as a key, and the concatenation of the transfer.status
, transfer.amount
, deposit.tracking_id
, meta.time
fields as message. Refer to the example below for a callback verification example.
Callback verification example
Copy <? php
# set API user login and password
$login = 'Your API key' ;
$password = 'Your API secret' ;
# parse callback data
$callback_payload = json_decode (
'{
"data": {
"type": "payout",
"id": "26",
"attributes": {
"address": "2NBr9k5xhvE2PxAAiFuczqQkeN76ShMdRZ6",
"created_at": "2021-09-30T13:01:49.930612Z",
"tracking_id": "12",
"fee_amount": "0.00000823",
"is_fee_included": false,
"amount": "0.00010000",
"destination": {
"address_type": "legacy",
"address": "2NBr9k5xhvE2PxAAiFuczqQkeN76ShMdRZ6"
}
},
"relationships": {
"wallet": {
"data": {
"type": "wallet",
"id": "19"
}
},
"currency": {
"data": {
"type": "currency",
"id": "1000"
}
},
"transfer": {
"data": {
"type": "transfer",
"id": "145"
}
}
}
},
"included": [
{
"type": "currency",
"id": "1000",
"attributes": {
"iso": 1000,
"name": "Bitcoin",
"alpha": "BTC",
"alias": null,
"exp": 8,
"confirmation_blocks": 3,
"minimal_transfer_amount": "0.00000546",
"block_delay": 3600
}
},
{
"type": "transfer",
"id": "145",
"attributes": {
"confirmations": 3,
"risk": 0,
"risk_status": 4,
"op_id": 26,
"op_type": 2,
"amount": "0.00010000",
"commission": "0.00000000",
"fee": "0.00000823",
"txid": "c3cc36f4569fdbfaacdbc14647e5046d9f239ab1af0268b531a5a213411a8fc9",
"status": 2,
"message": null,
"user_message": null,
"created_at": "2021-09-30T13:01:50.273446Z",
"updated_at": "2021-09-30T13:02:33.743770Z"
},
"relationships": {
"currency": {
"data": {
"type": "currency",
"id": "1000"
}
}
}
}
],
"meta": {
"time": "2021-09-30T13:02:34.059939+00:00",
"sign": "8ef2a0f0c6826895593d0d137cf6ce7353a4bbe999d4a6c363f92f1e9d7f8e32"
}
}' ,
true
) ;
$callback_sign = $callback_payload[ 'meta' ][ 'sign' ];
$callback_time = $callback_payload[ 'meta' ][ 'time' ];
# retrieve transfer and payout attributes
$included_transfer = array_filter (
$callback_payload[ 'included' ] ,
function ($item) {
return $item[ 'type' ] === 'transfer' ;
}
) ;
$included_transfer = array_pop ( $included_transfer ) [ 'attributes' ];
$payout = $callback_payload[ 'data' ][ 'attributes' ];
$status = $included_transfer[ 'status' ];
$amount = $included_transfer[ 'amount' ];
$tracking_id = $payout[ 'tracking_id' ];
# prepare data for hash check
$message = $status . $amount . $tracking_id . $callback_time;
$hash_secret = hash ( 'sha256' , $login . $password , true ) ;
$hash_hmac_result = hash_hmac ( 'sha256' , $message , $hash_secret ) ;
# print result
if ($hash_hmac_result === $callback_sign) {
echo 'Verified' ;
} else {
echo 'Invalid sign' ;
}
echo PHP_EOL ;
After processing the payload, you should send back the HTTP 200
response code without a body. If your server is temporarily unavailable or the status of the response is different from 200
, the system will resend the callback several times with the increasing delay time. The number of re-sendings is limited. If the manual resending is required, you can do it on the Wallet management > Events page in the Web UI.
Callback structure
The callback consists of three parts:
meta
— the metadata used for callback authentication. Contains the following fields:
time
(string) — the date and time when the callback was sent.
sign
(string) — the HMAC signature for a callback authentication. Refer to the Callback processing section for a callback verification example.
Callback body example
Copy {
"data" : {
"type" : "payout" ,
"id" : "26" ,
"attributes" : {
"address" : "2NBr9k5xhvE2PxAAiFuczqQkeN76ShMdRZ6" ,
"created_at" : "2021-09-30T13:01:49.930612Z" ,
"tracking_id" : "12" ,
"fee_amount" : "0.00000823" ,
"is_fee_included" : false ,
"amount" : "0.00010000" ,
"destination" : {
"address_type" : "legacy" ,
"address" : "2NBr9k5xhvE2PxAAiFuczqQkeN76ShMdRZ6"
}
} ,
"relationships" : {
"wallet" : {
"data" : {
"type" : "wallet" ,
"id" : "19"
}
} ,
"currency" : {
"data" : {
"type" : "currency" ,
"id" : "1000"
}
} ,
"transfer" : {
"data" : {
"type" : "transfer" ,
"id" : "145"
}
}
}
} ,
"included" : [
{
"type" : "currency" ,
"id" : "1000" ,
"attributes" : {
"iso" : 1000 ,
"name" : "Bitcoin" ,
"alpha" : "BTC" ,
"alias" : null ,
"exp" : 8 ,
"confirmation_blocks" : 3 ,
"minimal_transfer_amount" : "0.00000546" ,
"block_delay" : 3600
}
} ,
{
"type" : "transfer" ,
"id" : "145" ,
"attributes" : {
"confirmations" : 3 ,
"risk" : 0 ,
"risk_status" : 4 ,
"op_id" : 26 ,
"op_type" : 2 ,
"amount" : "0.00010000" ,
"commission" : "0.00000000" ,
"fee" : "0.00000823" ,
"txid" : "c3cc36f4569fdbfaacdbc14647e5046d9f239ab1af0268b531a5a213411a8fc9" ,
"status" : 2 ,
"message" : null ,
"user_message" : null ,
"created_at" : "2021-09-30T13:01:50.273446Z" ,
"updated_at" : "2021-09-30T13:02:33.743770Z"
} ,
"relationships" : {
"currency" : {
"data" : {
"type" : "currency" ,
"id" : "1000"
}
}
}
}
] ,
"meta" : {
"time" : "2021-09-30T13:02:34.059939+00:00" ,
"sign" : "8ef2a0f0c6826895593d0d137cf6ce7353a4bbe999d4a6c363f92f1e9d7f8e32"
}
}
Payout options
Request
OPT
[base]/payout
No request parameters.
Request example
cURL Python PHP
Copy curl --request OPTIONS \
--url [base]/payout/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/vnd.api+json' \
Copy import requests
import json
url = '[base]/payout/'
headers = {
'Content-Type' : 'application/vnd.api+json' ,
'Authorization' : 'Bearer <token>'
}
response = requests . request ( 'OPTIONS' , url, headers = headers)
print (response.text)
Copy <? php
$client = new Client ();
$headers = [
'Content-Type' => 'application/vnd.api+json' ,
'Authorization' => 'Bearer <token>'
];
$request = new Request ( 'OPTIONS' , '[base]/payout/' , $headers , $body);
$res = $client -> sendAsync ( $request ) -> wait () ;
echo $res -> getBody () ;
Response
In case of success, the response body contains an array of available methods along with a list of field parameters such as labels, min and max values, regular expressions used for validation, and so on.
Response codes
Response body example
Copy {
"data" : {
"renders" : [
"application/vnd.api+json"
] ,
"parses" : [
"application/vnd.api+json" ,
"multipart/form-data"
] ,
"allowed_methods" : [
"GET" ,
"POST" ,
"HEAD" ,
"OPTIONS"
] ,
"actions" : {
"GET" : {
"currency" : {
"lookup_expr" : "exact" ,
"regexp" : "^[1-9][0-9]*$" ,
"max_length" : 8 ,
"required" : false
} ,
"wallet" : {
"lookup_expr" : "exact" ,
"required" : false
} ,
"wallet_from" : {
"lookup_expr" : "exact" ,
"max_length" : 64 ,
"required" : false
} ,
"wallet_to" : {
"lookup_expr" : "exact" ,
"max_length" : 64 ,
"required" : false
} ,
"wallet_type" : {
"lookup_expr" : "exact" ,
"choices" : [
{
"value" : 1 ,
"display_name" : "Merchant"
} ,
{
"value" : 2 ,
"display_name" : "Enterprise"
} ,
{
"value" : 3 ,
"display_name" : "Custody"
} ,
{
"value" : 4 ,
"display_name" : "NFT"
} ,
{
"value" : 5 ,
"display_name" : "Swap"
}
] ,
"required" : false
} ,
"id" : {
"lookup_expr" : "exact" ,
"regexp" : "^[1-9][0-9]*$" ,
"max_length" : 8 ,
"required" : false
}