Esta página aún no está disponible en tu idioma.
This document describes the endpoints for initiating cryptocurrency withdrawals.
Initiate Withdrawal
Section titled “Initiate Withdrawal”Initiates a withdrawal of funds from your merchant account to an external wallet address.
Request
Section titled “Request”POST /v1/withdrawalsHeaders
Section titled “Headers”| Name | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
Merchant | Yes | Your merchant ID |
Timestamp | Yes | Unix timestamp in seconds. Must be within 5 minutes of server time. X-Timestamp is also accepted. |
Sign | Yes | HMAC-SHA256 withdrawal signature (see Authentication) |
Generate the canonical JSON body described in Authentication, sign it, and send that same string as the request body:
Sign = hmac_sha256(base64(canonical_json_body) + "." + Timestamp, API_KEY)Request Parameters
Section titled “Request Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
network | string | Yes | Blockchain network (e.g., “tron”, “bsc”) |
coin | string | Yes | Cryptocurrency (e.g., “usdt”, “usdc”) |
amount | string | Yes | Amount to withdraw in actual value (the minimum depends on the selected asset/network pair, e.g., “50.5” for 50.5 USDT) |
to_address | string | Yes | Destination wallet address |
Asset catalog: Use
GET /v1/coins-and-feeto fetch the currently enabled coin/network pairs, network fees, and minimum withdrawal values. These values can change. The withdrawal endpoint remains the final validation authority for the pair and amount.
See Supported Coins & Networks for the coin/network tables and guidance on displaying withdrawal options.
Example Request
Section titled “Example Request”{ "network": "tron", "coin": "usdt", "amount": "50.75", "to_address": "TXHXiQ2aXYqtz3y9gEGiVjzxXxXechdLwE"}Example Response
Section titled “Example Response”{ "data": { "success": true, "withdrawal_id": "5118a064-d5b9-4fab-ad14-8cf42f0020de", "message": "Withdrawal request submitted successfully", "status": "pending", "amounts": { "requested": { "crypto": "100.00", "usd": "100.00" }, "net": { "crypto": "98.15", "usd": "98.15" } }, "fees": { "merchant": { "crypto": "0.45", "usd": "0.45" }, "network": { "crypto": "1.40", "usd": "1.40" }, "total": { "crypto": "1.85", "usd": "1.85" } }, "fee_payer": { "merchant_fee": false, "network_fee": false } }, "status": "success"}Response Field Descriptions
Section titled “Response Field Descriptions”| Field | Type | Description |
|---|---|---|
data | object | Response data container |
data.success | boolean | Whether the withdrawal request succeeded |
data.withdrawal_id | string | Unique withdrawal identifier |
data.message | string | Human-readable message |
data.status | string | Current withdrawal status (pending, processing, completed, failed) |
data.amounts | object | Requested and net withdrawal amounts |
data.amounts.requested.crypto | string | Requested amount in cryptocurrency |
data.amounts.requested.usd | string | Requested amount in USD |
data.amounts.net.crypto | string | Net amount after fees in cryptocurrency |
data.amounts.net.usd | string | Net amount after fees in USD |
data.fees | object | Fee breakdown |
data.fees.merchant.crypto | string | Merchant fee in cryptocurrency |
data.fees.merchant.usd | string | Merchant fee in USD |
data.fees.network.crypto | string | Network fee in cryptocurrency |
data.fees.network.usd | string | Network fee in USD |
data.fees.total.crypto | string | Total fee in cryptocurrency |
data.fees.total.usd | string | Total fee in USD |
data.fee_payer.merchant_fee | boolean | Whether merchant pays the merchant fee |
data.fee_payer.network_fee | boolean | Whether merchant pays the network fee |
status | string | Overall response status (“success” or “error”) |
If your merchant account has the “Merchant Pays” settings enabled, the fields in fee_payer would be true and the amounts.net.crypto would equal the requested amounts.requested.crypto.
Error Responses
Section titled “Error Responses”| HTTP status | Response shape | Description |
|---|---|---|
400 | {"error":"Missing headers"} or {"error":"Missing timestamp"} | Required authentication input is missing |
400 | endpoint error with status and message | Includes withdrawal-permission denial and balance/fee rejection |
401 | authentication {"error":"..."} | Invalid merchant, signature, or expired timestamp |
403 | authentication or endpoint error | Merchant status blocks withdrawals |
422 | validation error with errors, status, and message | Pair, amount, or destination validation failed |
Example Error Response
Section titled “Example Error Response”{ "status": "error", "message": "Insufficient available balance"}Withdrawal Limits
Section titled “Withdrawal Limits”Each asset/network pair has current minimum and maximum amounts. Fetch the
current discovery catalog immediately before presenting withdrawal choices and
handle 422 as the authoritative answer if configuration changes between
discovery and submission. The public schema intentionally does not publish a
closed asset enum or static limit table.
Merchant Settings for Withdrawals
Section titled “Merchant Settings for Withdrawals”To use the Withdrawals API, you must first enable withdrawals in your merchant settings by checking the “Allow Withdrawals” checkbox. The description in settings reads: “Enable this to allow withdrawals via API”. This setting is required before any withdrawal API calls will work.
Additionally, there are two important settings that control how withdrawal fees are handled:
-
“Pay Withdrawal Network Fee” - The description in settings reads: “Enable this if merchant will pay withdrawal network fee”. When enabled, the network fee is deducted from the merchant’s balance instead of the withdrawal amount.
-
“Pay Withdrawal Service Fee” - The description in settings reads: “Enable this if merchant will pay withdrawal service fee”. When enabled, the merchant fee is deducted from the merchant’s balance instead of the withdrawal amount.
If both checkboxes are enabled, the recipient will receive exactly the amount specified in the API request, as all fees will be deducted from your merchant balance instead of the withdrawal amount.
Withdrawal Fees
Section titled “Withdrawal Fees”When initiating withdrawals, two types of fees apply:
- Network Fee: A fixed fee for processing transactions on the blockchain
- Merchant Fee: A percentage-based fee applied to the withdrawal amount
Fee Discovery
Section titled “Fee Discovery”Use GET /v1/merchants/fees for merchant-specific percentages and fee-payer
settings, and GET /v1/coins-and-fee for current network-fee discovery. The
accepted withdrawal response is the authoritative fee snapshot for that
withdrawal.
Fee Calculation Examples
Section titled “Fee Calculation Examples”The following arithmetic is illustrative only; substitute live fee values.
Scenario 1: Recipient Amount Is Reduced by Fees
When both “Pay Withdrawal Service Fee” and “Pay Withdrawal Network Fee” checkboxes are disabled:
Withdrawal amount: 100 USDTNetwork fee: 1.4 USDT (fixed)Merchant fee: 100 × 0.45% = 0.45 USDTTotal fees: 1.4 + 0.45 = 1.85 USDTAmount sent to destination: 100 - 1.85 = 98.15 USDTDeducted from merchant balance: 0 USDTScenario 2: Merchant Pays All Fees
When both “Pay Withdrawal Service Fee” and “Pay Withdrawal Network Fee” checkboxes are enabled:
Withdrawal amount: 100 USDTNetwork fee: 1.4 USDT (fixed)Merchant fee: 100 × 0.45% = 0.45 USDTTotal fees: 1.4 + 0.45 = 1.85 USDTAmount sent to destination: 100 USDT (full amount)Deducted from merchant balance: 1.85 USDT (all fees)The response includes the fields fee_payer.merchant_fee and fee_payer.network_fee to indicate which fee payment options are active for your merchant account. These correspond directly to the “Pay Withdrawal Service Fee” and “Pay Withdrawal Network Fee” settings.
Additional Notes
Section titled “Additional Notes”- Withdrawals are processed asynchronously
- The HTTP
200response contains the current projected status, commonlypendingorprocessing; it is not final settlement - You will receive
withdrawal.in_progress,withdrawal.success, orwithdrawal.failedwebhooks - The
to_addressmust be valid for the selected network - Always double-check the destination address before initiating a withdrawal
- There is no request
Idempotency-Key. If the outcome is unknown, reconcile by the returned/stored withdrawal ID or contact support instead of automatically submitting the same withdrawal again.
Code Examples
Section titled “Code Examples”Generate withdrawal signatures only in trusted server-side code. Do not expose merchant API keys in browsers, mobile apps, or public frontend bundles.
<?php// API credentials$merchantId = 'your_merchant_id';$apiKey = 'your_api_key';
// Withdrawal data$withdrawalData = [ 'network' => 'tron', 'coin' => 'usdt', 'amount' => '50.75', 'to_address' => 'TXHXiQ2aXYqtz3y9gEGiVjzxXxXechdLwE'];
// Generate signature$jsonData = json_encode($withdrawalData);$base64Data = base64_encode($jsonData);$timestamp = time();$signature = hash_hmac('sha256', $base64Data . '.' . $timestamp, $apiKey);
// Set up request$ch = curl_init('https://api.coinssend.com/v1/withdrawals');curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_POST, true);curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Merchant: ' . $merchantId, 'Timestamp: ' . $timestamp, 'Sign: ' . $signature]);
// Execute request$response = curl_exec($ch);$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);
// Process responseif ($httpCode >= 200 && $httpCode < 300) { $responseData = json_decode($response, true);
if ($responseData['data']['success'] === true) { echo "Withdrawal initiated successfully!\n"; echo "Withdrawal ID: " . $responseData['data']['withdrawal_id'] . "\n"; echo "Status: " . $responseData['data']['status'] . "\n"; echo "Message: " . $responseData['data']['message'] . "\n"; }} else { $errorData = json_decode($response, true); echo "Error: " . ($errorData['error'] ?? $errorData['message'] ?? 'Unknown error') . "\n";}// API credentialsconst merchantId = 'your_merchant_id';const apiKey = 'your_api_key';
// Withdrawal dataconst withdrawalData = { 'network': 'tron', 'coin': 'usdt', 'amount': '50.75', 'to_address': 'TXHXiQ2aXYqtz3y9gEGiVjzxXxXechdLwE'};
const crypto = require('node:crypto');
// Generate signatureconst jsonData = JSON.stringify(withdrawalData) .replace(/\//g, '\\/') .replace(/[\u0080-\uFFFF]/g, character => `\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}` );const base64Data = Buffer.from(jsonData).toString('base64');const timestamp = Math.floor(Date.now() / 1000).toString();const signature = crypto .createHmac('sha256', apiKey) .update(`${base64Data}.${timestamp}`) .digest('hex');
// Make the API requestfetch('https://api.coinssend.com/v1/withdrawals', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Merchant': merchantId, 'Timestamp': timestamp, 'Sign': signature }, body: jsonData}).then(response => { if (!response.ok) { return response.json().then(errorData => { throw new Error(errorData.error || errorData.message || `HTTP error! status: ${response.status}`); }); } return response.json();}).then(data => { if (data.data.success) { console.log("Withdrawal initiated successfully!"); console.log(`Withdrawal ID: ${data.data.withdrawal_id}`); console.log(`Status: ${data.data.status}`); console.log(`Message: ${data.data.message}`); }}).catch(error => { console.error('Withdrawal error:', error.message);});import requestsimport jsonimport base64import hashlibimport hmacimport time
# API credentialsmerchant_id = 'your_merchant_id'api_key = 'your_api_key'
# Withdrawal datawithdrawal_data = { 'network': 'tron', 'coin': 'usdt', 'amount': '50.75', 'to_address': 'TXHXiQ2aXYqtz3y9gEGiVjzxXxXechdLwE'}
def canonical_json(payload): encoded = json.dumps( payload, ensure_ascii=False, separators=(',', ':'), allow_nan=False ) escaped = [] for character in encoded: codepoint = ord(character) if character == '/': escaped.append(r'\/') elif codepoint < 0x80: escaped.append(character) elif codepoint <= 0xFFFF: escaped.append(f'\\u{codepoint:04x}') else: codepoint -= 0x10000 escaped.append(f'\\u{0xD800 + (codepoint >> 10):04x}') escaped.append(f'\\u{0xDC00 + (codepoint & 0x3FF):04x}') return ''.join(escaped)
# Generate signature from the exact body that will be sentjson_data = canonical_json(withdrawal_data)base64_data = base64.b64encode(json_data.encode()).decode()timestamp = str(int(time.time()))signature = hmac.new( api_key.encode(), f'{base64_data}.{timestamp}'.encode(), hashlib.sha256).hexdigest()
# Set up headersheaders = { 'Content-Type': 'application/json', 'Merchant': merchant_id, 'Timestamp': timestamp, 'Sign': signature}
# Make the API requesttry: response = requests.post( 'https://api.coinssend.com/v1/withdrawals', headers=headers, data=json_data )
# Check for HTTP errors response.raise_for_status()
# Parse the response response_data = response.json()
if response_data.get('data', {}).get('success'): print("Withdrawal initiated successfully!") print(f"Withdrawal ID: {response_data['data']['withdrawal_id']}") print(f"Status: {response_data['data']['status']}") print(f"Message: {response_data['data']['message']}") else: print(f"Error: {response_data.get('message', 'Unknown error')}")
except requests.exceptions.HTTPError as e: error_data = {} try: error_data = response.json() except: pass
error_message = error_data.get('error') or error_data.get('message') or str(e) print(f"HTTP Error: {error_message}")
except requests.exceptions.RequestException as e: print(f"Request Error: {str(e)}")For more details on working with withdrawals, see the Node.js withdrawal example.