curl --request POST \
--url https://secureapi.tender.cash/v1/api/wallet/transfer \
--header 'Content-Type: <content-type>' \
--header 'authorization: <api-key>' \
--header 'x-access-id: <x-access-id>' \
--header 'x-request-id: <x-request-id>' \
--header 'x-timestamp: <x-timestamp>' \
--data '
{
"chain": "tron",
"currency": "usdt",
"receiver": "TABCef...",
"amount": 50,
"merchant_reference": "acme-user-8821",
"address_reference": "acme-user-8821-slot-2",
"payment_reference": "payout-9922"
}
'const options = {
method: 'POST',
headers: {
authorization: '<api-key>',
'x-timestamp': '<x-timestamp>',
'x-request-id': '<x-request-id>',
'x-access-id': '<x-access-id>',
'Content-Type': '<content-type>'
},
body: JSON.stringify({
chain: 'tron',
currency: 'usdt',
receiver: 'TABCef...',
amount: 50,
merchant_reference: 'acme-user-8821',
address_reference: 'acme-user-8821-slot-2',
payment_reference: 'payout-9922'
})
};
fetch('https://secureapi.tender.cash/v1/api/wallet/transfer', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://secureapi.tender.cash/v1/api/wallet/transfer"
payload = {
"chain": "tron",
"currency": "usdt",
"receiver": "TABCef...",
"amount": 50,
"merchant_reference": "acme-user-8821",
"address_reference": "acme-user-8821-slot-2",
"payment_reference": "payout-9922"
}
headers = {
"authorization": "<api-key>",
"x-timestamp": "<x-timestamp>",
"x-request-id": "<x-request-id>",
"x-access-id": "<x-access-id>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://secureapi.tender.cash/v1/api/wallet/transfer",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'chain' => 'tron',
'currency' => 'usdt',
'receiver' => 'TABCef...',
'amount' => 50,
'merchant_reference' => 'acme-user-8821',
'address_reference' => 'acme-user-8821-slot-2',
'payment_reference' => 'payout-9922'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: <content-type>",
"authorization: <api-key>",
"x-access-id: <x-access-id>",
"x-request-id: <x-request-id>",
"x-timestamp: <x-timestamp>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"message": "success",
"data": {
"confirmation_count": 0,
"tx_id": "0xabc123..."
}
}{
"status": "success",
"message": "success",
"data": {
"confirmation_count": 0,
"operation_id": "f3b0c2d1e4a5..."
}
}{
"status": "error",
"message": "Unsupported currency for chain tron",
"data": null
}{
"status": "error",
"message": "INVALID_REQUEST_SIGNATURE",
"data": null
}{
"status": "error",
"message": "Wallet address reference not found for this merchant",
"data": null
}{
"status": "error",
"message": "Too many requests. Max 120 per 60s.",
"data": null
}{
"status": "error",
"message": "Wallet service is temporarily unavailable. Please try again.",
"data": null
}Transfer Funds
Send native coins or tokens from a subwallet address
curl --request POST \
--url https://secureapi.tender.cash/v1/api/wallet/transfer \
--header 'Content-Type: <content-type>' \
--header 'authorization: <api-key>' \
--header 'x-access-id: <x-access-id>' \
--header 'x-request-id: <x-request-id>' \
--header 'x-timestamp: <x-timestamp>' \
--data '
{
"chain": "tron",
"currency": "usdt",
"receiver": "TABCef...",
"amount": 50,
"merchant_reference": "acme-user-8821",
"address_reference": "acme-user-8821-slot-2",
"payment_reference": "payout-9922"
}
'const options = {
method: 'POST',
headers: {
authorization: '<api-key>',
'x-timestamp': '<x-timestamp>',
'x-request-id': '<x-request-id>',
'x-access-id': '<x-access-id>',
'Content-Type': '<content-type>'
},
body: JSON.stringify({
chain: 'tron',
currency: 'usdt',
receiver: 'TABCef...',
amount: 50,
merchant_reference: 'acme-user-8821',
address_reference: 'acme-user-8821-slot-2',
payment_reference: 'payout-9922'
})
};
fetch('https://secureapi.tender.cash/v1/api/wallet/transfer', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://secureapi.tender.cash/v1/api/wallet/transfer"
payload = {
"chain": "tron",
"currency": "usdt",
"receiver": "TABCef...",
"amount": 50,
"merchant_reference": "acme-user-8821",
"address_reference": "acme-user-8821-slot-2",
"payment_reference": "payout-9922"
}
headers = {
"authorization": "<api-key>",
"x-timestamp": "<x-timestamp>",
"x-request-id": "<x-request-id>",
"x-access-id": "<x-access-id>",
"Content-Type": "<content-type>"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://secureapi.tender.cash/v1/api/wallet/transfer",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'chain' => 'tron',
'currency' => 'usdt',
'receiver' => 'TABCef...',
'amount' => 50,
'merchant_reference' => 'acme-user-8821',
'address_reference' => 'acme-user-8821-slot-2',
'payment_reference' => 'payout-9922'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: <content-type>",
"authorization: <api-key>",
"x-access-id: <x-access-id>",
"x-request-id: <x-request-id>",
"x-timestamp: <x-timestamp>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"message": "success",
"data": {
"confirmation_count": 0,
"tx_id": "0xabc123..."
}
}{
"status": "success",
"message": "success",
"data": {
"confirmation_count": 0,
"operation_id": "f3b0c2d1e4a5..."
}
}{
"status": "error",
"message": "Unsupported currency for chain tron",
"data": null
}{
"status": "error",
"message": "INVALID_REQUEST_SIGNATURE",
"data": null
}{
"status": "error",
"message": "Wallet address reference not found for this merchant",
"data": null
}{
"status": "error",
"message": "Too many requests. Max 120 per 60s.",
"data": null
}{
"status": "error",
"message": "Wallet service is temporarily unavailable. Please try again.",
"data": null
}x-access-id, x-request-id, x-timestamp, and an HMAC-SHA256
authorization signature. See Authentication.currency.
Supported chains: ethereum, tron, bitcoin
Transfer types and responses
| Transfer | When | HTTP | data |
|---|---|---|---|
| Coin | currency is the chain’s native coin | 200 | { tx_id } |
| Token | currency is a token, sent on-chain directly | 200 | { confirmation_count, tx_id } |
| Gasless token | currency is a token and the network runs token transfers gasless | 202 | { confirmation_count, operation_id } |
200 means the transaction was broadcast. Tender sends a sub_user_wallet_transfer_confirmed
webhook when it confirms on-chain.
A 202 means a gasless operation was accepted but has not settled. Either poll
Get Transfer Status with the operation_id,
or wait for the sub_user_wallet_transfer_confirmed or sub_user_wallet_transfer_failed webhook.
Gasless token transfers spend from the address slot’s gasless_address, so the tokens must be
held there.
payment_reference is stored for reconciliation but does
not block duplicates. If a request times out, check
List Wallet Transactions for your
payment_reference before you retry.Example
// Token transfer (USDT on Tron)
const res = await fetch(`${BASE}/wallet/transfer`, {
method: 'POST',
headers: makeHeaders(),
body: JSON.stringify({
chain: 'tron',
currency: 'usdt',
receiver: 'TABCef...',
amount: 50,
merchant_reference: 'acme-user-8821', // the wallet reference
address_reference: 'acme-user-8821-slot-2', // the address slot to send from
payment_reference: 'payout-9922', // your reference for this transfer
}),
});
const body = await res.json();
if (res.status === 202) {
// Gasless: body.data → { confirmation_count: 0, operation_id: "f3b0c2..." }
} else {
// On-chain: body.data.tx_id → "a1b2c3..."
}
res = requests.post(
f"{BASE}/wallet/transfer",
headers=make_headers(),
json={
"chain": "ethereum",
"currency": "ethereum",
"receiver": "0x999fff...",
"amount": 0.01,
"merchant_reference": "acme-user-8821",
"address_reference": "3f6c1e9a-2b7d-4e8f-9a10-5c4b3a2d1e0f",
"payment_reference": "payout-9921",
},
)
data = res.json()["data"]
# res.status_code == 200, data["tx_id"] → "0xabc123..."
Headers
{"timeStamp":"<x-timestamp>","requestId":"<x-request-id>","accessId":"<x-access-id>"}, signed with your access secret. The request body is not part of the signature.Example: "pV3n0o8kq0lK2mJ8bZ1yq3Yt6hR0cXK9u4mVJ2l9sE0=""2026-10-15T09:45:53.000Z""550e8400-e29b-41d4-a716-446655440000""your-access-id-here""application/json"Body
ethereum, tron or bitcoin.Example: "tron"usdt, usdc, or the native coin ID (ethereum, tron,
bitcoin). It must be supported on chain.Example: "usdt""TABCef..."0.01 ETH or 50 USDT).Example: 50reference you passed to
Create Wallet).Example: "acme-user-8821"address_reference of the address slot to send from. Together with merchant_reference
and chain, it identifies the source address.Example: "acme-user-8821-slot-2""payout-9922"address
or gasless_address. For a gasless token transfer, omit it or pass the gasless_address;
passing the slot’s address is rejected. We recommend omitting sender: the source address is
already determined by merchant_reference, address_reference and chain.Example: "TLq9..."Response
"success""success"Show data properties
Show data properties
"0xabc123def456...""f3b0c2d1e4a5..."0 at submission. Present for token transfers.Example: 0Error responses
| Condition | Status | Message |
|---|---|---|
| Missing or invalid field | 400 | Validation message, e.g. payment_reference must be a string |
| Invalid chain | 400 | invalid chain: <chain> |
| Invalid currency | 400 | invalid currency: <currency> |
| Chain not supported for subwallets | 400 | Unsupported chain for subwallets |
| Currency not valid on that chain | 400 | Unsupported currency for chain <chain> |
| No contract for the token on that chain | 400 | no contract address for <currency> on <chain> |
sender doesn’t match the address slot | 400 | Transfer address does not match any known address for this wallet |
| Insufficient balance or gas, gasless address required, or other transfer rejection | 400 | Message describing the problem |
| No address for that wallet, slot and chain | 404 | Wallet address reference not found for this merchant |
| Wallet service unavailable | 503 | Wallet service is temporarily unavailable. Please try again. |
503 doesn’t tell you whether the transfer was broadcast. Check
List Wallet Transactions before retrying.Authorizations
Signed (HMAC) authentication. Required headers: x-access-id, x-request-id (UUID v4), x-timestamp (ISO 8601, must be within 5 minutes of server time), and authorization: the Base64 HMAC-SHA256 of the JSON string {"timeStamp":"","requestId":"","accessId":""} (keys in that order, no whitespace) using your access secret. The request body, method and path are not signed. The API key must be linked to an outlet.
Headers
Base64-encoded HMAC-SHA256 signature of {"timeStamp":"","requestId":"","accessId":""}, signed with your access secret. The request body is not part of the signature.
ISO 8601 timestamp; must be within 5 minutes of server time
A unique UUID v4 per request
Body
ethereum, tron, bitcoin Currency ID: usdt, usdc, or the native coin ID (ethereum, tron, bitcoin).
"usdt"
"TABCef..."
Human-readable units.
50
The wallet reference to send from.
"acme-user-8821"
The address slot to send from.
"acme-user-8821-slot-2"
Your reference for this transfer; echoed in webhooks and the transaction list. Not an idempotency key.
"payout-9922"
Optional cross-check; must match the slot's address or gasless_address. Recommended to omit.