Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dfns.co/llms.txt

Use this file to discover all available pages before exploring further.

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

ParameterRequiredDescription
kindYesTransfer type: Native, Erc20, Erc721, etc.
toYesRecipient address
amountYesAmount in base units (wei for ETH)
externalIdNoYour 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

StatusDescription
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

Monitor transactions

Polling patterns

Set up webhooks

Real-time notifications

Manage policies via API

Handle policy approvals
Last modified on April 30, 2026