This guide covers how to create transfers, handle different asset types, and monitor transfer status using the Dfns API and SDK.
Prerequisites
Service account or authenticated user with Wallets:Transfers:Create permission
Dfns SDK installed and configured
Wallet with sufficient balance for the transfer and gas fees
Creating a native token transfer
Send native tokens (ETH, MATIC, SOL, etc.):
import { DfnsApiClient } from '@dfns/sdk'
const dfns = new DfnsApiClient ({
baseUrl: 'https://api.dfns.io' ,
// Your signer configuration
})
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Native' ,
to: '0x1234...recipient' ,
amount: '1000000000000000000' // 1 ETH in wei
}
})
console . log ( 'Transfer ID:' , transfer . id )
console . log ( 'Status:' , transfer . status )
Request parameters
Parameter Required Description kindYes Transfer type: Native, Erc20, Erc721, etc. toYes Recipient address amountYes Amount in base units (wei for ETH) externalIdNo Your reference ID for tracking
Creating an ERC-20 token transfer
Send ERC-20 tokens:
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Erc20' ,
contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' , // USDC
to: '0x1234...recipient' ,
amount: '1000000' // 1 USDC (6 decimals)
}
})
Token amounts are in base units. USDC has 6 decimals, so 1 USDC = 1000000. ETH has 18 decimals, so 1 ETH = 1000000000000000000.
Creating an NFT transfer
Transfer ERC-721 NFTs:
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Erc721' ,
contract: '0x...nft-contract' ,
to: '0x1234...recipient' ,
tokenId: '123'
}
})
Transfer with external ID
Use external IDs for your own tracking:
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Native' ,
to: '0x1234...recipient' ,
amount: '1000000000000000000' ,
externalId: 'payout-2024-001' // Your reference
}
})
Checking transfer status
Get the status of a transfer:
const transfer = await dfns . wallets . getTransfer ({
walletId: 'wa-xxx-xxx' ,
transferId: 'tr-xxx-xxx'
})
console . log ( 'Status:' , transfer . status )
// 'Pending', 'Broadcasted', 'Confirmed', 'Failed'
Transfer statuses
Status Description PendingTransfer created, may be awaiting approval BroadcastedTransaction sent to the network ConfirmedTransaction confirmed on the blockchain FailedTransaction failed
Listing transfers
List transfers for a wallet:
const transfers = await dfns . wallets . listTransfers ({
walletId: 'wa-xxx-xxx'
})
for ( const transfer of transfers . items ) {
console . log ( ` ${ transfer . id } : ${ transfer . status } ` )
}
Handling policy approvals
If a transfer triggers a policy that requires approval, the transfer enters a Pending state:
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Native' ,
to: '0x1234...recipient' ,
amount: '100000000000000000000' // Large amount
}
})
if ( transfer . status === 'Pending' ) {
console . log ( 'Transfer requires approval' )
// The transfer will execute automatically once approved
}
See managing policies for working with approvals programmatically.
Monitoring with webhooks
Subscribe to transfer events for real-time updates:
// Subscribe to events
await dfns . webhooks . createWebhook ({
body: {
url: 'https://your-app.com/webhooks/dfns' ,
events: [
'wallet.transfer.broadcasted' ,
'wallet.transfer.confirmed' ,
'wallet.transfer.failed'
],
status: 'Enabled'
}
})
Handle webhook events:
app . post ( '/webhooks/dfns' , async ( req , res ) => {
const event = req . body
switch ( event . kind ) {
case 'wallet.transfer.confirmed' :
await handleTransferConfirmed ( event . data )
break
case 'wallet.transfer.failed' :
await handleTransferFailed ( event . data )
break
}
res . status ( 200 ). send ( 'OK' )
})
See webhooks guide for complete webhook setup.
Polling for status
Alternative to webhooks - poll for transfer status:
async function waitForConfirmation ( walletId : string , transferId : string ) {
while ( true ) {
const transfer = await dfns . wallets . getTransfer ({
walletId ,
transferId
})
if ( transfer . status === 'Confirmed' ) {
return transfer
}
if ( transfer . status === 'Failed' ) {
throw new Error ( `Transfer failed: ${ transfer . error } ` )
}
// Wait before checking again
await new Promise ( resolve => setTimeout ( resolve , 5000 ))
}
}
See transaction monitoring for polling best practices.
Error handling
Handle common errors:
try {
const transfer = await dfns . wallets . createTransfer ({
walletId: 'wa-xxx-xxx' ,
body: {
kind: 'Native' ,
to: '0x1234...recipient' ,
amount: '1000000000000000000'
}
})
} catch ( error ) {
if ( error . status === 403 ) {
console . error ( 'Permission denied' )
} else if ( error . status === 400 ) {
console . error ( 'Invalid request:' , error . message )
} else if ( error . message . includes ( 'insufficient balance' )) {
console . error ( 'Insufficient balance for transfer' )
} else {
console . error ( 'Error creating transfer:' , error )
}
}
Transfer API Complete API reference
Transaction monitoring Polling patterns
Webhooks Real-time notifications
Managing policies Handle policy approvals