En bref
L’API Withdraw de Blockradar vous permet d’envoyer des actifs stablecoin depuis vos portefeuilles vers des adresses blockchain externes. Elle prend en charge les retraits individuels, les retraits par lot vers plusieurs destinataires, et fournit une estimation des frais avant l’exécution.
Prérequis
Avant d’utiliser l’API Withdraw, assurez-vous d’avoir :
Portefeuille créé
Créez un portefeuille via l’API Create Wallet ou depuis le tableau de bord. Vous aurez besoin du walletId pour les opérations de retrait.
ID d'actif
Récupérez l’assetId du token que vous souhaitez retirer depuis Assets dans le tableau de bord ou via l’API Get Assets .
Solde suffisant
Assurez-vous que votre portefeuille dispose d’un solde suffisant de l’actif à retirer, ainsi que du token natif (ETH, BNB, MATIC, etc.) pour couvrir les frais de réseau.
L’API Withdraw envoie des actifs stablecoin depuis votre portefeuille Blockradar vers n’importe quelle adresse blockchain externe :
Retrait individuel Envoyez des actifs à une adresse destinataire en un seul appel API.
Retrait par lot Envoyez des actifs à plusieurs destinataires en un seul appel API, réduisant la surcharge et simplifiant les paiements en masse.
Estimation des frais Calculez les frais de réseau avant l’exécution pour garantir un solde suffisant et afficher les coûts aux utilisateurs.
Mode Sign-Only Signez les transactions sans les diffuser pour des cas d’usage avancés tels que la signature hors ligne ou l’envoi personnalisé.
Master Wallet vs Child Address
L’API Withdraw est disponible à deux niveaux :
Master Wallet Retirez directement depuis votre master wallet. Idéal pour les opérations de trésorerie et la gestion centralisée des fonds.
Child Address Retirez depuis des child addresses individuelles. Parfait pour les opérations spécifiques aux utilisateurs et la gestion ségréguée des fonds.
Endpoints
Opération Master Wallet Child Address Withdraw POST /v1/wallets/{walletId}/withdrawPOST /v1/wallets/{walletId}/addresses/{addressId}/withdrawFrais Réseau POST /v1/wallets/{walletId}/withdraw/network-feePOST /v1/wallets/{walletId}/addresses/{addressId}/withdraw/network-feeSign-Only POST /v1/wallets/{walletId}/withdraw/signPOST /v1/wallets/{walletId}/addresses/{addressId}/withdraw/sign
Retrait individuel
Envoyez des actifs à une seule adresse destinataire.
Paramètres de la requête
Paramètre Type Requis Description assetIdstring Oui* L’UUID de l’actif à retirer. Requis si le tableau assets n’est pas fourni. addressstring Oui* L’adresse de destination. Requise si le tableau assets n’est pas fourni. amountstring Oui* Le montant du retrait. Doit être supérieur à 0. Requis si le tableau assets n’est pas fourni. referencestring Non Votre identifiant interne de suivi pour le retrait. notestring Non Un message court ou une remarque interne. Non visible on-chain. metadataobject Non Paires clé-valeur personnalisées pour des détails supplémentaires de la transaction.
Les paramètres marqués d’un * sont requis pour les retraits individuels mais ne sont pas nécessaires si vous utilisez le tableau assets pour les retraits par lot.
Exemple de retrait individuel
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/withdraw \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assetId": "asset-uuid-here",
"address": "0xRecipientAddress...",
"amount": "100",
"reference": "payout-12345",
"note": "Monthly payout",
"metadata": {
"userId": "user_abc123",
"payoutType": "salary"
}
}'
const withdrawal = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /withdraw` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId: 'asset-uuid-here' ,
address: '0xRecipientAddress...' ,
amount: '100' ,
reference: 'payout-12345' ,
note: 'Monthly payout' ,
metadata: {
userId: 'user_abc123' ,
payoutType: 'salary'
}
})
}
). then ( r => r . json ());
console . log ( 'Withdrawal initiated:' , withdrawal . data );
Réponse du retrait individuel
{
"message" : "Withdrawal initiated successfully" ,
"statusCode" : 200 ,
"data" : {
"id" : "tx-uuid-123" ,
"hash" : "0xabc123..." ,
"status" : "PENDING" ,
"amount" : "100" ,
"recipientAddress" : "0xRecipientAddress..." ,
"reference" : "payout-12345" ,
"note" : "Monthly payout" ,
"metadata" : {
"userId" : "user_abc123" ,
"payoutType" : "salary"
},
"createdAt" : "2024-01-15T10:30:00Z"
}
}
Retraits par lot
Envoyez des actifs à plusieurs destinataires en un seul appel API. Les retraits par lot sont exécutés séquentiellement, et chaque retrait est traité comme une transaction blockchain distincte.
Quand utiliser les retraits par lot
Paiements en masse : Payez plusieurs employés, fournisseurs ou partenaires en une seule fois
Distributions : Envoyez des actifs à plusieurs adresses
Transferts multi-destinataires : Envoyez des montants différents à des adresses différentes
Efficacité opérationnelle : Réduisez les appels API et simplifiez la logique de paiement
Paramètres de la requête par lot
Pour les retraits par lot, utilisez le tableau assets au lieu des paramètres individuels :
Paramètre Type Requis Description assetsarray Oui Tableau d’objets de retrait (max 20 par lot)
Chaque élément du tableau assets :
Champ Type Requis Description idstring Oui L’UUID de l’actif à retirer addressstring Oui L’adresse de destination amountstring Oui Le montant du retrait. Doit être supérieur à 0. referencestring Non Note de référence optionnelle pour ce retrait notestring Non Un message court ou une remarque interne. Non visible on-chain. metadataobject Non Paires clé-valeur personnalisées pour des détails supplémentaires de la transaction.
Exemple de retrait par lot
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/withdraw \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assets": [
{
"id": "asset-uuid-usdc",
"address": "0xRecipient1...",
"amount": "100",
"reference": "payout-001",
"note": "January salary"
},
{
"id": "asset-uuid-usdc",
"address": "0xRecipient2...",
"amount": "150",
"reference": "payout-002",
"note": "January salary"
},
{
"id": "asset-uuid-usdt",
"address": "0xRecipient3...",
"amount": "200",
"reference": "payout-003",
"note": "Vendor payment"
}
]
}'
const batchWithdrawal = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /withdraw` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assets: [
{
id: 'asset-uuid-usdc' ,
address: '0xRecipient1...' ,
amount: '100' ,
reference: 'payout-001' ,
note: 'January salary'
},
{
id: 'asset-uuid-usdc' ,
address: '0xRecipient2...' ,
amount: '150' ,
reference: 'payout-002' ,
note: 'January salary'
},
{
id: 'asset-uuid-usdt' ,
address: '0xRecipient3...' ,
amount: '200' ,
reference: 'payout-003' ,
note: 'Vendor payment'
}
]
})
}
). then ( r => r . json ());
console . log ( 'Batch withdrawal result:' , batchWithdrawal . data );
Réponse du retrait par lot
{
"message" : "Batch withdrawal initiated successfully" ,
"statusCode" : 200 ,
"data" : {
"success" : [
{
"index" : 0 ,
"id" : "tx-uuid-1" ,
"hash" : "0xabc..." ,
"status" : "PENDING" ,
"amount" : "100" ,
"recipientAddress" : "0xRecipient1..." ,
"reference" : "payout-001"
},
{
"index" : 1 ,
"id" : "tx-uuid-2" ,
"hash" : "0xdef..." ,
"status" : "PENDING" ,
"amount" : "150" ,
"recipientAddress" : "0xRecipient2..." ,
"reference" : "payout-002"
},
{
"index" : 2 ,
"id" : "tx-uuid-3" ,
"hash" : "0xghi..." ,
"status" : "PENDING" ,
"amount" : "200" ,
"recipientAddress" : "0xRecipient3..." ,
"reference" : "payout-003"
}
],
"errors" : []
}
}
Gestion des échecs partiels
Les retraits par lot prennent en charge le succès partiel. Si certains retraits échouent, les autres seront tout de même exécutés :
const result = await batchWithdrawal . json ();
// Process successful withdrawals
result . data . success . forEach ( tx => {
console . log ( `✓ ${ tx . reference } : ${ tx . hash } ` );
});
// Handle failed withdrawals
result . data . errors . forEach ( error => {
console . error ( `✗ Index ${ error . index } : ${ error . error } ` );
// Retry logic or error reporting here
});
Règles des retraits par lot
Règle Valeur Taille maximale du lot 20 retraits par requête Taille minimale du lot 1 retrait Ordre d’exécution Séquentiel Gestion des erreurs Succès partiel (les échecs n’arrêtent pas les retraits suivants)
Estimation des frais réseau
Estimez toujours les frais avant d’exécuter des retraits afin de garantir un solde suffisant en token natif et d’afficher des coûts précis aux utilisateurs.
Estimation des frais individuels
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/withdraw/network-fee \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assetId": "asset-uuid-here",
"address": "0xRecipientAddress...",
"amount": "100"
}'
const fee = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /withdraw/network-fee` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId: 'asset-uuid-here' ,
address: '0xRecipientAddress...' ,
amount: '100'
})
}
). then ( r => r . json ());
console . log ( 'Network fee:' , fee . data . networkFee );
console . log ( 'Fee in USD:' , fee . data . networkFeeInUSD );
Réponse pour les frais individuels
{
"message" : "Network fee fetched successfully" ,
"statusCode" : 200 ,
"data" : {
"networkFee" : "0.00001247904" ,
"networkFeeInUSD" : "0.01" ,
"transactionFee" : "0" ,
"nativeBalance" : "0.5" ,
"nativeBalanceInUSD" : "450.00" ,
"estimatedArrivalTime" : 30
}
}
Estimation des frais par lot
Estimez les frais pour plusieurs retraits à la fois :
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/withdraw/network-fee \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assets": [
{
"id": "asset-uuid-1",
"address": "0xRecipient1...",
"amount": "100"
},
{
"id": "asset-uuid-2",
"address": "0xRecipient2...",
"amount": "50"
}
]
}'
const batchFee = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /withdraw/network-fee` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assets: [
{
id: 'asset-uuid-1' ,
address: '0xRecipient1...' ,
amount: '100'
},
{
id: 'asset-uuid-2' ,
address: '0xRecipient2...' ,
amount: '50'
}
]
})
}
). then ( r => r . json ());
console . log ( 'Total network fee:' , batchFee . data . totalNetworkFee );
console . log ( 'Total fee in USD:' , batchFee . data . totalNetworkFeeInUSD );
Réponse pour les frais par lot
{
"message" : "Network fee fetched successfully" ,
"statusCode" : 200 ,
"data" : {
"fees" : [
{
"index" : 0 ,
"assetId" : "asset-uuid-1" ,
"address" : "0xRecipient1..." ,
"amount" : "100" ,
"networkFee" : "0.00001247904" ,
"transactionFee" : "0" ,
"estimatedArrivalTime" : 30
},
{
"index" : 1 ,
"assetId" : "asset-uuid-2" ,
"address" : "0xRecipient2..." ,
"amount" : "50" ,
"networkFee" : "0.00000504" ,
"transactionFee" : "0" ,
"estimatedArrivalTime" : 30
}
],
"totalNetworkFee" : "0.00001751904" ,
"totalNetworkFeeInUSD" : "0.02" ,
"totalTransactionFee" : "0" ,
"nativeBalance" : "0.073690520542044578" ,
"nativeBalanceInUSD" : "66.54" ,
"estimatedArrivalTime" : 60 ,
"errors" : []
}
}
Champs de la réponse de frais
Champ Description networkFeeFrais de gas en unités de token natif (retrait individuel) networkFeeInUSDFrais de gas convertis en USD (retrait individuel) feesTableau des estimations individuelles de frais (retrait par lot) totalNetworkFeeSomme de tous les frais réseau (retrait par lot) totalNetworkFeeInUSDTotal des frais réseau en USD (retrait par lot) transactionFeeFrais de transaction de la plateforme (le cas échéant) nativeBalanceSolde actuel du token natif nativeBalanceInUSDSolde du token natif en USD estimatedArrivalTimeTemps de confirmation estimé en secondes errorsTableau des estimations échouées (retrait par lot)
Mode Sign-Only
Signez des transactions sans les diffuser sur la blockchain. Utile pour :
Signature hors ligne : Préparez les transactions pour un envoi ultérieur
Flux multi-signatures : Collectez les signatures avant l’envoi
Inspection de transaction : Vérifiez les détails de la transaction avant de la diffuser
Exemple de Sign-Only
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/withdraw/sign \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assetId": "asset-uuid-here",
"address": "0xRecipientAddress...",
"amount": "100"
}'
const signedTx = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /withdraw/sign` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId: 'asset-uuid-here' ,
address: '0xRecipientAddress...' ,
amount: '100'
})
}
). then ( r => r . json ());
console . log ( 'Signed transaction:' , signedTx . data . signedTransaction );
Réponse Sign-Only
{
"message" : "Transaction signed successfully" ,
"statusCode" : 200 ,
"data" : {
"id" : "tx-uuid-123" ,
"signedTransaction" : "0xf86c..." ,
"status" : "SIGNED" ,
"amount" : "100" ,
"recipientAddress" : "0xRecipientAddress..." ,
"createdAt" : "2024-01-15T10:30:00Z"
}
}
Retraits depuis une Child Address
Retirez depuis des child addresses individuelles plutôt que depuis la master wallet :
curl --request POST \
--url https://api.blockradar.co/v1/wallets/{walletId}/addresses/{addressId}/withdraw \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '{
"assetId": "asset-uuid-here",
"address": "0xRecipientAddress...",
"amount": "50",
"reference": "user-withdrawal-123"
}'
const childWithdrawal = await fetch (
`https://api.blockradar.co/v1/wallets/ ${ walletId } /addresses/ ${ addressId } /withdraw` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId: 'asset-uuid-here' ,
address: '0xRecipientAddress...' ,
amount: '50' ,
reference: 'user-withdrawal-123'
})
}
). then ( r => r . json ());
console . log ( 'Child address withdrawal:' , childWithdrawal . data );
Les retraits depuis une child address prennent également en charge les opérations par lot via le tableau assets, en suivant le même format que les retraits par lot depuis la master wallet.
Événements de Webhook
Surveillez l’achèvement des retraits via les webhooks :
Événement Description withdraw.successRetrait terminé et confirmé sur la blockchain withdraw.failedLe retrait n’a pas pu être exécuté withdraw.cancelledLe retrait a été annulé avant son achèvement
Payload du Webhook
{
"event" : "withdraw.success" ,
"data" : {
"id" : "081d6315-159f-4c38-b02a-c4708836c5bd" ,
"reference" : "payout-12345" ,
"senderAddress" : "0x947514e4B803e312C312da0F1B41fEDdbe15ae7a" ,
"recipientAddress" : "0x2455eC6700092991Ce0782365A89d5Cd89c8Fa22" ,
"amount" : "100" ,
"amountPaid" : "100" ,
"fee" : null ,
"currency" : "USD" ,
"blockNumber" : 6928833 ,
"hash" : "0x5fcb7dd11cbb5a6d64da08cf7e0d63c1a1e7b9d1b89e3e8d1c6a5f4b3a2c1d0e" ,
"status" : "SUCCESS" ,
"type" : "WITHDRAW" ,
"note" : "Monthly payout" ,
"asset" : {
"id" : "asset-uuid" ,
"name" : "USD Coin" ,
"symbol" : "USDC" ,
"decimals" : 6
},
"wallet" : {
"id" : "wallet-uuid" ,
"name" : "Main Treasury"
},
"blockchain" : {
"id" : "blockchain-uuid" ,
"name" : "ethereum" ,
"network" : "mainnet"
},
"metadata" : {
"userId" : "user_abc123" ,
"payoutType" : "salary"
},
"createdAt" : "2024-01-15T10:30:00Z" ,
"updatedAt" : "2024-01-15T10:31:00Z"
}
}
Exemple de flux complet
Voici une implémentation complète du flux : estimation des frais → confirmation de l’utilisateur → retrait :
async function processWithdrawal ( walletId , assetId , recipientAddress , amount ) {
const apiKey = process . env . BLOCKRADAR_API_KEY ;
const baseUrl = 'https://api.blockradar.co/v1' ;
// Step 1: Estimate network fee
const feeResponse = await fetch (
` ${ baseUrl } /wallets/ ${ walletId } /withdraw/network-fee` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId ,
address: recipientAddress ,
amount
})
}
). then ( r => r . json ());
// Step 2: Check if we have enough native balance for gas
const fee = feeResponse . data ;
if ( parseFloat ( fee . nativeBalance ) < parseFloat ( fee . networkFee )) {
throw new Error ( `Insufficient gas: need ${ fee . networkFee } , have ${ fee . nativeBalance } ` );
}
// Step 3: Display fee to user (in your UI)
console . log ( `Network fee: ${ fee . networkFee } (≈$ ${ fee . networkFeeInUSD } )` );
console . log ( `Estimated time: ${ fee . estimatedArrivalTime } s` );
// Step 4: Execute withdrawal (after user confirmation)
const withdrawal = await fetch (
` ${ baseUrl } /wallets/ ${ walletId } /withdraw` ,
{
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
'x-api-key' : apiKey
},
body: JSON . stringify ({
assetId ,
address: recipientAddress ,
amount ,
reference: `withdraw- ${ Date . now () } `
})
}
). then ( r => r . json ());
console . log ( 'Withdrawal initiated:' , withdrawal . data . id );
console . log ( 'Transaction hash:' , withdrawal . data . hash );
// Step 5: Listen for webhook to confirm completion
return withdrawal . data ;
}
// Usage
processWithdrawal (
'wallet-uuid' ,
'asset-uuid-usdc' ,
'0xRecipientAddress...' ,
'100'
);
Réponses d’erreur
{
"message" : "Insufficient balance for withdrawal" ,
"statusCode" : 400 ,
"error" : "INSUFFICIENT_BALANCE" ,
"data" : {
"required" : "100" ,
"available" : "50.25" ,
"asset" : "USDC"
}
}
{
"message" : "Insufficient native token balance for gas" ,
"statusCode" : 400 ,
"error" : "INSUFFICIENT_GAS" ,
"data" : {
"required" : "0.005" ,
"available" : "0.001" ,
"token" : "ETH"
}
}
{
"message" : "Invalid recipient address" ,
"statusCode" : 400 ,
"error" : "INVALID_ADDRESS" ,
"data" : {
"address" : "invalid_address_here"
}
}
{
"message" : "Asset not found" ,
"statusCode" : 404 ,
"error" : "ASSET_NOT_FOUND" ,
"data" : {
"assetId" : "invalid-asset-uuid"
}
}
{
"message" : "Amount must be greater than 0" ,
"statusCode" : 400 ,
"error" : "INVALID_AMOUNT" ,
"data" : {
"amount" : "0"
}
}
{
"message" : "Batch size exceeds maximum limit" ,
"statusCode" : 400 ,
"error" : "BATCH_SIZE_EXCEEDED" ,
"data" : {
"maximum" : 20 ,
"provided" : 25
}
}
Bonnes pratiques
Sécurité
Validez les adresses : Vérifiez toujours les adresses des destinataires avant d’initier les retraits
Utilisez des références : Suivez les retraits avec des identifiants de référence uniques pour la réconciliation
Mettez en place des webhooks : Écoutez les événements withdraw.success et withdraw.failed pour confirmer le statut
Vérifiez l’AML : Blockradar filtre automatiquement les adresses ; examinez toute transaction signalée
Gestion des frais
Estimez avant l’exécution : Appelez toujours l’endpoint network-fee avant les retraits
Surveillez le solde natif : Assurez-vous d’avoir suffisamment d’ETH/BNB/MATIC pour les frais de gas
Utilisez les lots pour plus d’efficacité : Regroupez plusieurs retraits pour réduire les appels API et la charge opérationnelle
Gestion des erreurs
Gérez les échecs partiels : Dans les retraits par lot, vérifiez à la fois les tableaux success et errors
Mettez en place des nouvelles tentatives : Utilisez un backoff exponentiel pour les défaillances transitoires
Journalisez toutes les transactions : Stockez les identifiants et hashes de transactions pour le débogage et la réconciliation
Utilisez des tailles de lot appropriées : Des lots plus grands réduisent les appels API mais augmentent le temps de chaque requête
Mettez en cache les IDs d’actifs : Stockez les IDs d’actifs localement pour éviter les recherches répétées
Mettez en place une limitation de débit : Respectez les limites de débit de l’API pour éviter le throttling
Référence API
Endpoints Master Wallet
Endpoint Description Withdraw Exécuter un retrait individuel ou par lot Network Fee Estimer les frais de retrait Sign-Only Signer sans diffuser
Endpoints Child Address
Endpoint Description Withdraw Exécuter un retrait individuel ou par lot Network Fee Estimer les frais de retrait Sign-Only Signer sans diffuser
Support
L’API Withdraw fournit une interface flexible pour envoyer des actifs stablecoin vers des adresses externes. Commencez par les retraits individuels et l’estimation des frais, puis intégrez les opérations par lot pour les paiements en masse à mesure que vos besoins grandissent.