Withdrawals
The withdrawals API allows you to process payouts to customers and external addresses. This guide covers withdrawal creation, status tracking, balance validation, and best practices for managing withdrawal flows.
Overview
The withdrawal flow consists of these steps:
- Validate balance - Ensure sufficient funds are available
- Create withdrawal - Submit withdrawal request with destination address
- Monitor status - Track withdrawal processing via webhooks and API calls
- Handle confirmations - Process completed withdrawals in your system
Creating Withdrawals
POST /api/withdrawals
Create a new withdrawal request to send funds to a destination address.
Request
curl -X POST "https://api.dcepay.io/api/withdrawals" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"token": "BTC",
"amount": 0.001,
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"metadata": {
"orderId": "order_123",
"customerEmail": "[email protected]"
}
}'Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Cryptocurrency symbol (BTC, ETH, USDT, etc.) |
amount | number | Yes | Amount to withdraw |
address | string | Yes | Destination address for the withdrawal |
metadata | object | No | Additional data to associate with the withdrawal |
Response
{
"id": "wth_xxxxxxxxxxxxxxxx",
"token": "BTC",
"amount": 0.001,
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"status": "pending",
"fee": 0.00001,
"netAmount": 0.00099,
"createdAt": "2024-12-19T10:30:00Z",
"estimatedCompletion": "2024-12-19T11:30:00Z",
"metadata": {
"orderId": "order_123",
"customerEmail": "[email protected]"
}
}JavaScript Example
async function createWithdrawal(token, amount, address, metadata = {}) {
const response = await fetch('https://api.dcepay.io/api/withdrawals', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DCE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
token,
amount,
address,
metadata
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Withdrawal creation failed: ${error.message}`);
}
return response.json();
}
// Usage
const withdrawal = await createWithdrawal('BTC', 0.001, 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', {
orderId: 'order_123',
customerEmail: '[email protected]'
});
console.log('Withdrawal ID:', withdrawal.id);
console.log('Net amount (after fees):', withdrawal.netAmount);Listing Withdrawals
GET /api/withdrawals
Retrieve a list of your withdrawals with optional filtering.
Request
curl -X GET "https://api.dcepay.io/api/withdrawals?status=CONFIRMED&page=1&limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status (pending, processing, confirmed, failed) |
token | string | Filter by cryptocurrency |
limit | number | Number of results to return (default: 20, max: 100) |
offset | number | Number of results to skip for pagination |
startDate | string | Filter withdrawals created after this date (ISO 8601) |
endDate | string | Filter withdrawals created before this date (ISO 8601) |
Response
{
"withdrawals": [
{
"id": "wth_xxxxxxxxxxxxxxxx",
"token": "BTC",
"amount": 0.001,
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"status": "confirmed",
"fee": 0.00001,
"netAmount": 0.00099,
"txHash": "0x1234567890abcdef...",
"confirmedAt": "2024-12-19T11:45:00Z",
"createdAt": "2024-12-19T10:30:00Z",
"metadata": {
"orderId": "order_123",
"customerEmail": "[email protected]"
}
}
],
"pagination": {
"total": 75,
"limit": 10,
"offset": 0,
"hasMore": true
}
}Balance Validation
Before creating a withdrawal, always check your available balance:
GET /api/balance
curl -X GET "https://api.dcepay.io/api/balance" \
-H "Authorization: Bearer YOUR_API_KEY"Response
{
"balances": [
{
"token": "BTC",
"available": 0.005,
"pending": 0.001,
"total": 0.006
},
{
"token": "ETH",
"available": 0.1,
"pending": 0.02,
"total": 0.12
}
]
}Balance Validation Example
async function validateWithdrawalBalance(token, amount) {
const response = await fetch('https://api.dcepay.io/api/balance', {
headers: {
'Authorization': `Bearer ${process.env.DCE_API_KEY}`
}
});
const { balances } = await response.json();
const balance = balances.find(b => b.token === token);
if (!balance) {
throw new Error(`No balance found for ${token}`);
}
if (balance.available < amount) {
throw new Error(`Insufficient balance. Available: ${balance.available} ${token}, Requested: ${amount} ${token}`);
}
return true;
}
// Usage
try {
await validateWithdrawalBalance('BTC', 0.001);
const withdrawal = await createWithdrawal('BTC', 0.001, 'address...');
console.log('Withdrawal created:', withdrawal.id);
} catch (error) {
console.error('Withdrawal failed:', error.message);
}Withdrawal Status Tracking
Status Values
| Status | Description |
|---|---|
pending | Withdrawal created, waiting for processing |
processing | Withdrawal is being processed by the network |
confirmed | Withdrawal completed and confirmed on blockchain |
failed | Withdrawal failed (insufficient funds, invalid address, etc.) |
cancelled | Withdrawal was cancelled |
Status Transitions
pending → processing (when withdrawal starts)
processing → confirmed (when blockchain confirms)
pending → failed (if validation fails)
processing → failed (if network issues occur)Fee Structure
Withdrawals incur fees that are deducted from the withdrawal amount. The fee structure consists of a base fee (1 USDT) plus an optional markup rate.
Fee Calculation
The withdrawal fee is calculated as follows:
- Base Fee: 1 USDT (fixed)
- Markup Rate: Optional percentage markup on the withdrawal amount
- Total Fee: Base Fee + (Withdrawal Amount × Markup Rate)
// Example fee calculations
const baseFee = 1; // USDT
const markupRate = 0.05; // 5%
// For a 100 USDT withdrawal with 5% markup:
const withdrawalAmount = 100;
const markupAmount = withdrawalAmount * markupRate; // 5 USDT
const totalFee = baseFee + markupAmount; // 6 USDT
const netAmount = withdrawalAmount - totalFee; // 94 USDT
// For a 100 USDT withdrawal with no markup:
const totalFeeNoMarkup = baseFee; // 1 USDT
const netAmountNoMarkup = withdrawalAmount - totalFeeNoMarkup; // 99 USDTSystem Configuration
Withdrawal fees are configured internally by system administrators and cannot be modified via the API. The fee structure is determined by:
- Base Fee: Configurable system setting (default: 1 USDT)
- Markup Rate: Configurable system setting (default: 0% - no markup)
- Fee Status: Can be enabled/disabled by administrators
Admin API for Fee Management
Administrators can manage withdrawal fee settings using the admin API:
Get Current Settings:
GET /api/admin/withdrawal-fees
Authorization: Bearer <admin-api-key>Update Settings:
POST /api/admin/withdrawal-fees
Authorization: Bearer <admin-api-key>
Content-Type: application/json
{
"baseFee": "1.5",
"markupRate": "0.02",
"enabled": true
}Test Fee Calculation:
PUT /api/admin/withdrawal-fees
Authorization: Bearer <admin-api-key>
Content-Type: application/json
{
"amount": "100",
"currency": "USDT"
}Fee Information in Response
The withdrawal API response includes detailed fee information:
{
"success": true,
"withdrawalId": "wth_xxxxxxxxxxxxxxxx",
"amount": "100.00",
"currency": "USDT",
"feeInfo": {
"grossAmount": "100.00",
"netAmount": "94.00",
"totalFees": "6.00",
"feeBreakdown": {
"baseFee": "1.00",
"markupRate": "5.00",
"markupAmount": "5.00",
"totalFee": "6.00"
},
"feePercentage": "6.00"
}
}Webhook Notifications
You'll receive webhook notifications when withdrawal status changes:
Withdrawal Confirmed Webhook
{
"event": "withdrawal.confirmed",
"data": {
"id": "wth_xxxxxxxxxxxxxxxx",
"token": "USDT",
"amount": "100.00",
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"status": "confirmed",
"fee": "6.00",
"netAmount": "94.00",
"commission": "6.00",
"commissionRate": "0.05",
"txHash": "0x1234567890abcdef...",
"confirmedAt": "2024-12-19T11:45:00Z",
"metadata": {
"orderId": "order_123",
"customerEmail": "[email protected]",
"feeCalculation": {
"baseFee": "1.00",
"markupRate": "0.05",
"totalFee": "6.00",
"netAmount": "94.00"
}
}
},
"timestamp": "2024-12-19T11:45:00Z"
}Withdrawal Failed Webhook
{
"event": "withdrawal.failed",
"data": {
"id": "wth_xxxxxxxxxxxxxxxx",
"token": "BTC",
"amount": 0.001,
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"status": "failed",
"failedAt": "2024-12-19T11:45:00Z",
"reason": "insufficient_funds",
"metadata": {
"orderId": "order_123",
"customerEmail": "[email protected]"
}
},
"timestamp": "2024-12-19T11:45:00Z"
}Error Handling
Common Errors
| Status Code | Error | Description |
|---|---|---|
| 400 | INVALID_ADDRESS | Invalid destination address |
| 400 | INSUFFICIENT_BALANCE | Not enough funds for withdrawal |
| 400 | INVALID_AMOUNT | Amount is too small or invalid |
| 400 | ADDRESS_NOT_WHITELISTED | Address not in whitelist (if enabled) |
| 429 | RATE_LIMITED | Too many withdrawal requests |
Example Error Response
{
"error": "INSUFFICIENT_BALANCE",
"message": "Available balance: 0.0005 BTC, Requested: 0.001 BTC",
"statusCode": 400
}Best Practices
1. Address Validation
Always validate addresses before creating withdrawals:
function validateAddress(token, address) {
const patterns = {
'BTC': /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$|^bc1[a-z0-9]{39,59}$/,
'ETH': /^0x[a-fA-F0-9]{40}$/,
'USDT': /^0x[a-fA-F0-9]{40}$/
};
const pattern = patterns[token];
if (!pattern) {
throw new Error(`Unsupported token: ${token}`);
}
if (!pattern.test(address)) {
throw new Error(`Invalid ${token} address: ${address}`);
}
return true;
}2. Balance Checking
Always check balance before withdrawal:
async function safeWithdrawal(token, amount, address, metadata = {}) {
// 1. Validate address
validateAddress(token, address);
// 2. Check balance
await validateWithdrawalBalance(token, amount);
// 3. Create withdrawal
const withdrawal = await createWithdrawal(token, amount, address, metadata);
// 4. Log withdrawal
console.log(`Withdrawal created: ${withdrawal.id}`);
console.log(`Amount: ${withdrawal.amount} ${token}`);
console.log(`Fee: ${withdrawal.fee} ${token}`);
console.log(`Net amount: ${withdrawal.netAmount} ${token}`);
return withdrawal;
}3. Idempotency
Use idempotency keys to prevent duplicate withdrawals:
async function createWithdrawalWithIdempotency(token, amount, address, metadata = {}) {
const idempotencyKey = generateIdempotencyKey(token, amount, address, metadata);
const response = await fetch('https://api.dcepay.io/api/withdrawals', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DCE_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey
},
body: JSON.stringify({
token,
amount,
address,
metadata
})
});
return response.json();
}
function generateIdempotencyKey(token, amount, address, metadata) {
const data = JSON.stringify({ token, amount, address, metadata });
return require('crypto').createHash('sha256').update(data).digest('hex');
}4. Webhook Handling
Handle withdrawal webhooks properly:
app.post('/webhooks', async (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'withdrawal.confirmed':
await handleWithdrawalConfirmed(data);
break;
case 'withdrawal.failed':
await handleWithdrawalFailed(data);
break;
default:
console.log(`Unhandled webhook event: ${event}`);
}
res.status(200).json({ received: true });
});
async function handleWithdrawalConfirmed(data) {
const { id, amount, address, txHash, metadata } = data;
// Update your database
await updateWithdrawalStatus(id, 'confirmed', txHash);
// Notify customer
await notifyCustomer(metadata.customerEmail, {
type: 'withdrawal_confirmed',
amount,
address,
txHash
});
}
async function handleWithdrawalFailed(data) {
const { id, reason, metadata } = data;
// Update your database
await updateWithdrawalStatus(id, 'failed', null, reason);
// Notify customer
await notifyCustomer(metadata.customerEmail, {
type: 'withdrawal_failed',
reason
});
}Integration Example
Here's a complete withdrawal service implementation:
class WithdrawalService {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.dcepay.io/api';
}
async createWithdrawal(token, amount, address, metadata = {}) {
// Validate inputs
this.validateAddress(token, address);
await this.validateBalance(token, amount);
const response = await fetch(`${this.baseUrl}/withdrawals`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': this.generateIdempotencyKey(token, amount, address, metadata)
},
body: JSON.stringify({
token,
amount,
address,
metadata
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Withdrawal creation failed: ${error.message}`);
}
return response.json();
}
async getWithdrawals(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(`${this.baseUrl}/withdrawals?${params}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
return response.json();
}
async getWithdrawalById(id) {
const response = await fetch(`${this.baseUrl}/withdrawals/${id}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
return response.json();
}
async validateBalance(token, amount) {
const response = await fetch(`${this.baseUrl}/balance`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const { balances } = await response.json();
const balance = balances.find(b => b.token === token);
if (!balance || balance.available < amount) {
throw new Error(`Insufficient balance for ${amount} ${token}`);
}
return true;
}
validateAddress(token, address) {
const patterns = {
'BTC': /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$|^bc1[a-z0-9]{39,59}$/,
'ETH': /^0x[a-fA-F0-9]{40}$/,
'USDT': /^0x[a-fA-F0-9]{40}$/
};
const pattern = patterns[token];
if (!pattern || !pattern.test(address)) {
throw new Error(`Invalid ${token} address: ${address}`);
}
}
generateIdempotencyKey(token, amount, address, metadata) {
const data = JSON.stringify({ token, amount, address, metadata });
return require('crypto').createHash('sha256').update(data).digest('hex');
}
}
// Usage
const withdrawalService = new WithdrawalService(process.env.DCE_API_KEY);
try {
const withdrawal = await withdrawalService.createWithdrawal('BTC', 0.001, 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', {
orderId: 'order_123',
customerEmail: '[email protected]'
});
console.log('Withdrawal created:', withdrawal.id);
console.log('Net amount:', withdrawal.netAmount);
} catch (error) {
console.error('Withdrawal failed:', error.message);
}Next Steps
Now that you understand withdrawals, explore:
- Balance Management - Monitor your account balance
- Settlements - Request bulk settlements
- Webhooks - Handle withdrawal notifications
Updated about 1 month ago
