🚧 Bloque documentation is under development

Transfers

Transfer funds between any accounts using the Bloque SDK.

Overview

The transfer API allows you to move funds between different account types:

  • Card to Virtual account
  • Card to Bancolombia account
  • Virtual to Virtual account
  • Bancolombia to Card account
  • And any other combination

All transfers are queued for processing and can be tracked using the returned queue ID.

Basic Transfer

Transfer funds between two accounts:

basic-transfer.ts
import { SDK } from '@bloque/sdk';

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'production',
});

const transfer = await bloque.accounts.transfer({
  sourceUrn: 'did:bloque:account:card:usr-123:crd-456',
  destinationUrn: 'did:bloque:account:virtual:acc-67890',
  amount: '1000000000000',
  asset: 'KSM/12',
  metadata: {
    reference: 'payment-123',
    note: 'Monthly subscription'
  }
});

console.log('Transfer queued:', transfer.queueId);
console.log('Status:', transfer.status);
console.log('Message:', transfer.message);

Parameters

basic-transfer.ts
interface TransferParams {
  /**
   * URN of the source account
   * @example "did:bloque:account:card:usr-123:crd-456"
   */
  sourceUrn: string;

  /**
   * URN of the destination account
   * @example "did:bloque:account:virtual:acc-67890"
   */
  destinationUrn: string;

  /**
   * Amount to transfer
   * @example "1000000000000"
   */
  amount: string;

  /**
   * Asset to transfer
   * Supported: 'DUSD/6' | 'KSM/12'
   * @example "KSM/12"
   */
  asset: 'DUSD/6' | 'KSM/12';

  /**
   * Optional metadata
   * @example { reference: "payment-123", note: "Payment description" }
   */
  metadata?: Record<string, unknown>;
}

Response

types.ts
interface TransferResult {
  queueId: string;    // Queue ID for tracking
  status: TransferStatus;    // Transfer status
  message: string;    // Status message
}
types.ts
type TransferStatus = 
  | 'queued'       // Transfer queued for processing
  | 'processing'   // Transfer in progress
  | 'completed'    // Transfer completed successfully
  | 'failed';      // Transfer failed

The transfer is queued for processing. Use queueId to track transfer status.

Transfer States and Transitions

StateDescriptionCan Transition To
queuedTransfer queued waiting for processingprocessing, failed
processingTransfer in progresscompleted, failed
completedTransfer completed successfully-
failedTransfer failed (insufficient funds, invalid account, etc.)-
stateDiagram-v2
    [*] --> queued
    queued --> processing
    queued --> failed
    processing --> completed
    processing --> failed
    completed --> [*]
    failed --> [*]

Transfer Examples

Card to Bancolombia

Transfer from a virtual card to a Bancolombia account:

card-to-bancolombia.ts
const transfer = await bloque.accounts.transfer({
  sourceUrn: 'did:bloque:account:card:usr-123:crd-456',
  destinationUrn: 'did:bloque:account:bancolombia:acc-12345',
  amount: '5000000', // 5 DUSD (6 decimals)
  asset: 'DUSD/6',
  metadata: {
    reference: 'withdrawal-001',
    type: 'savings'
  }
});

console.log(`Transfer queued: ${transfer.queueId}`);

Between Virtual Accounts

Transfer KSM between virtual accounts:

types.ts
const transfer = await bloque.accounts.transfer({
  sourceUrn: 'did:bloque:account:virtual:acc-11111',
  destinationUrn: 'did:bloque:account:virtual:acc-22222',
  amount: '2000000000000', // 2 KSM (12 decimals)
  asset: 'KSM/12',
  metadata: {
    reference: 'internal-transfer-42',
    department: 'operations'
  }
});

console.log(`KSM transfer queued: ${transfer.queueId}`);

Card to Card

Transfer between two cards:

types.ts
const transfer = await bloque.accounts.transfer({
  sourceUrn: 'did:bloque:account:card:usr-123:crd-111',
  destinationUrn: 'did:bloque:account:card:usr-456:crd-222',
  amount: '1000000', // 1 DUSD
  asset: 'DUSD/6',
  metadata: {
    reference: 'user-payment-001',
    note: 'Payment for services'
  }
});

Supported Assets

AssetDescriptionDecimalsExample Amount
DUSD/6Digital USD61000000 = 1 DUSD
KSM/12Kusama121000000000000 = 1 KSM

:::tip Decimal Conversion Always account for decimals when specifying amounts:

  • DUSD/6: Multiply by 10^6 (1 DUSD = 1,000,000)
  • KSM/12: Multiply by 10^12 (1 KSM = 1,000,000,000,000) :::

Transfer Metadata

Add custom metadata to track transfers:

types.ts
const transfer = await bloque.accounts.transfer({
  sourceUrn: sourceAccount,
  destinationUrn: destinationAccount,
  amount: '1000000',
  asset: 'DUSD/6',
  metadata: {
    // Payment tracking
    reference: 'INV-2025-001',
    invoiceId: 'inv_abc123',

    // Customer info
    customerId: 'cust_xyz789',
    customerName: 'John Doe',

    // Business context
    department: 'Sales',
    project: 'Q1-2025',
    category: 'commission',

    // Notes
    note: 'Sales commission for January 2025',
    approvedBy: 'manager@company.com'
  }
});

Error Handling

Always handle errors appropriately:

types.ts
try {
  const transfer = await bloque.accounts.transfer({
    sourceUrn: sourceAccount,
    destinationUrn: destinationAccount,
    amount: '1000000',
    asset: 'DUSD/6',
  });

  console.log('✓ Transfer queued:', transfer.queueId);

  // Store transfer info in your database
  await saveTransfer({
    queueId: transfer.queueId,
    status: transfer.status,
    sourceUrn: sourceAccount,
    destinationUrn: destinationAccount,
    amount: '1000000',
    asset: 'DUSD/6',
  });

} catch (error) {
  if (error instanceof Error) {
    console.error('Transfer failed:', error.message);

    // Handle specific errors
    if (error.message.includes('insufficient funds')) {
      // Not enough balance
    } else if (error.message.includes('not found')) {
      // Account doesn't exist
    } else if (error.message.includes('Invalid asset')) {
      // Unsupported asset
    }
  }

  throw error;
}

Complete Example

complete-transfer.ts
import { SDK } from '@bloque/sdk';

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'production',
});

async function transferFunds(
  fromUrn: string,
  toUrn: string,
  amount: string,
  asset: 'DUSD/6' | 'KSM/12'
) {
  try {
    // Check source account balance first
    const balances = await bloque.accounts.card.balance({
      urn: fromUrn,
    });

    const available = BigInt(balances[asset]?.current || '0');
    const transferAmount = BigInt(amount);

    if (available < transferAmount) {
      throw new Error('Insufficient funds');
    }

    console.log('✓ Balance check passed');

    // Execute transfer
    const transfer = await bloque.accounts.transfer({
      sourceUrn: fromUrn,
      destinationUrn: toUrn,
      amount,
      asset,
      metadata: {
        timestamp: new Date().toISOString(),
        initiatedBy: 'api-user'
      }
    });

    console.log('✓ Transfer queued');
    console.log('  Queue ID:', transfer.queueId);
    console.log('  Status:', transfer.status);

    return transfer;

  } catch (error) {
    console.error('✗ Transfer failed:', error);
    throw error;
  }
}

// Example usage
await transferFunds(
  'did:bloque:account:card:usr-123:crd-456',
  'did:bloque:account:virtual:acc-67890',
  '1000000', // 1 DUSD
  'DUSD/6'
);

Batch Transfers

Transfer funds to multiple destinations:

batch-transfer.ts
async function batchTransfer(
  sourceUrn: string,
  destinations: Array<{ urn: string; amount: string }>,
  asset: 'DUSD/6' | 'KSM/12'
) {
  const results = [];

  for (const dest of destinations) {
    try {
      const transfer = await bloque.accounts.transfer({
        sourceUrn,
        destinationUrn: dest.urn,
        amount: dest.amount,
        asset,
      });

      results.push({
        success: true,
        queueId: transfer.queueId,
        destination: dest.urn,
      });

      console.log(`✓ Transfer to ${dest.urn}: ${transfer.queueId}`);

    } catch (error) {
      results.push({
        success: false,
        error: error instanceof Error ? error.message : 'Unknown error',
        destination: dest.urn,
      });

      console.error(`✗ Transfer to ${dest.urn} failed:`, error);
    }
  }

  return results;
}

// Usage
const results = await batchTransfer(
  'did:bloque:account:card:usr-123:crd-456',
  [
    { urn: 'did:bloque:account:virtual:acc-111', amount: '1000000' },
    { urn: 'did:bloque:account:virtual:acc-222', amount: '2000000' },
    { urn: 'did:bloque:account:bancolombia:acc-333', amount: '3000000' },
  ],
  'DUSD/6'
);

console.log(`Completed: ${results.filter(r => r.success).length}/${results.length}`);

Best Practices

  1. Check Balances: Verify sufficient funds before transfers
  2. Validate URNs: Ensure account URNs are valid
  3. Asset Decimals: Account for proper decimal places
  4. Error Handling: Use try-catch blocks
  5. Metadata: Add tracking information
  6. Test First: Test in sandbox mode
  7. Store Queue IDs: Save queue IDs for tracking

Next Steps