🚧 Bloque documentation is under development

Virtual Accounts

Create and manage virtual testing accounts using the Bloque SDK.

Overview

Virtual accounts are simple testing accounts requiring only basic personal information. They're perfect for:

  • Development & Testing: Quick account creation for integration tests
  • Sandbox Environment: Safe testing without real financial data
  • Simple KYC: Minimal information required (first name, last name)
  • Fast Setup: Instant account creation

Creating a Virtual Account

Basic Creation

Create a virtual account with minimal information:

create-virtual-account.ts
import { SDK } from '@bloque/sdk';

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

// Connect to user session
const session = await bloque.connect('user-alias');

// Create a virtual account
const account = await session.accounts.virtual.create({
  firstName: 'John',
  lastName: 'Doe',
  metadata: {
    environment: 'testing',
    purpose: 'integration-test',
  },
});

console.log('Account created:', account.urn);
console.log('Status:', account.status);
console.log('Ledger ID:', account.ledgerId);

Parameters

types.ts
interface CreateVirtualAccountParams {
  firstName: string;       // Required: Account holder's first name
  lastName: string;        // Required: Account holder's last name
  ledgerId?: string;       // Optional: Ledger account ID (auto-created if not provided)
  webhookUrl?: string;     // Optional: Webhook for account events
  metadata?: Record<string, string>; // Optional: Custom metadata (must be strings)
}

Response

types.ts
interface VirtualAccount {
  urn: string;              // Unique resource name
  id: string;               // Account ID
  firstName: string;        // Account holder's first name
  lastName: string;         // Account holder's last name
  status: AccountStatus;    // Account status
  ownerUrn: string;         // Owner URN
  ledgerId: string;         // Ledger account ID
  webhookUrl: string | null;
  metadata?: Record<string, string>;
  createdAt: string;        // ISO 8601 timestamp
  updatedAt: string;        // ISO 8601 timestamp
}

type AccountStatus =
  | 'creation_in_progress'
  | 'active'
  | 'disabled'
  | 'frozen'
  | 'deleted'
  | 'creation_failed';

Managing Virtual Accounts

Update Metadata

Update custom metadata on a virtual account:

update-metadata.ts
const updated = await session.accounts.virtual.updateMetadata({
  urn: 'did:bloque:mediums:virtual:account:123e4567',
  metadata: {
    updated_by: 'admin',
    update_reason: 'testing_update',
    environment: 'staging',
  },
});

console.log('Metadata updated:', updated.metadata);
Metadata Restrictions

The source field is reserved and cannot be modified through metadata updates. All metadata values must be strings.

Account States

Manage the state of virtual accounts:

manage-state.ts
const accountUrn = 'did:bloque:mediums:virtual:account:123e4567';

// Activate account
const activated = await session.accounts.virtual.activate(accountUrn);
console.log('Status:', activated.status); // 'active'

// Freeze account (temporarily suspend)
const frozen = await session.accounts.virtual.freeze(accountUrn);
console.log('Status:', frozen.status); // 'frozen'

// Disable account (permanently)
const disabled = await session.accounts.virtual.disable(accountUrn);
console.log('Status:', disabled.status); // 'disabled'

Available States

StatusDescriptionCan Transition To
creation_in_progressAccount is being createdactive, creation_failed
activeAccount is active and usablefrozen, disabled, deleted
frozenAccount is temporarily suspendedactive, disabled
disabledAccount is permanently disabled-
deletedAccount has been deleted-
creation_failedAccount creation failed-
stateDiagram-v2
    [*] --> creation_in_progress
    creation_in_progress --> active
    creation_in_progress --> creation_failed
    active --> frozen
    active --> disabled
    active --> deleted
    frozen --> active
    frozen --> disabled
    disabled --> deleted
    creation_failed --> [*]
    deleted --> [*]

Use Cases

Integration Testing

integration-test.ts
import { SDK } from '@bloque/sdk';
import { describe, test, expect } from 'bun:test';

describe('Virtual Account Integration', () => {
  const bloque = new SDK({
    origin: 'your-origin',
    auth: { type: 'apiKey', apiKey: process.env.BLOQUE_API_KEY! },
    mode: 'sandbox',
  });

  test('should create and manage virtual account', async () => {
    const session = await bloque.connect('did:bloque:your-origin:test-user');

    // Create account
    const account = await session.accounts.virtual.create({
      firstName: 'Test',
      lastName: 'User',
      metadata: {
        test_id: 'integration-001',
      },
    });

    expect(account.status).toBe('active');
    expect(account.firstName).toBe('Test');

    // Update metadata
    const updated = await session.accounts.virtual.updateMetadata({
      urn: account.urn,
      metadata: {
        test_id: 'integration-001',
        updated: 'true',
      },
    });

    expect(updated.metadata?.updated).toBe('true');

    // Freeze account
    const frozen = await session.accounts.virtual.freeze(account.urn);
    expect(frozen.status).toBe('frozen');
  });
});

Automated Testing

test-factory.ts
class VirtualAccountFactory {
  constructor(private sdk: SDK) {}

  async createTestAccount(testId: string) {
    const session = await this.sdk.connect('did:bloque:your-origin:test-user');

    return await session.accounts.virtual.create({
      firstName: 'Test',
      lastName: `User-${testId}`,
      metadata: {
        test_id: testId,
        created_at: new Date().toISOString(),
        environment: 'test',
      },
    });
  }

  async cleanupTestAccount(urn: string) {
    const session = await this.sdk.connect('did:bloque:your-origin:test-user');
    await session.accounts.virtual.disable(urn);
  }
}

// Usage
const factory = new VirtualAccountFactory(bloque);
const account = await factory.createTestAccount('test-001');
// ... run tests ...
await factory.cleanupTestAccount(account.urn);

Error Handling

Handle errors appropriately when working with virtual accounts:

error-handling.ts
import {
  BloqueValidationError,
  BloqueNotFoundError,
  BloqueAuthenticationError,
} from '@bloque/sdk';

try {
  const account = await session.accounts.virtual.create({
    firstName: 'John',
    lastName: 'Doe',
  });

  console.log('Account created:', account.urn);
} catch (error) {
  if (error instanceof BloqueValidationError) {
    console.error('Validation failed:', error.validationErrors);
    // Handle invalid input (missing required fields, etc.)
  } else if (error instanceof BloqueNotFoundError) {
    console.error('Resource not found:', error.resourceId);
    // Handle missing user/session
  } else if (error instanceof BloqueAuthenticationError) {
    console.error('Authentication failed');
    // Handle auth issues
  } else {
    console.error('Unexpected error:', error);
  }
}

Complete Example

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

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'sandbox',
  timeout: 30000,
  retry: {
    enabled: true,
    maxRetries: 3,
  },
});

async function setupVirtualAccount(userId: string) {
  try {
    // Connect to user session
    const session = await bloque.connect(`did:bloque:your-origin:${userId}`);

    // Create virtual account
    const account = await session.accounts.virtual.create({
      firstName: 'John',
      lastName: 'Doe',
      webhookUrl: 'https://your-app.com/webhooks/virtual-account',
      metadata: {
        user_id: userId,
        account_type: 'testing',
        created_via: 'sdk',
      },
    });

    console.log('✓ Account created:', account.urn);
    console.log('✓ Account holder:', `${account.firstName} ${account.lastName}`);
    console.log('✓ Ledger ID:', account.ledgerId);
    console.log('✓ Status:', account.status);

    // Verify account is active
    if (account.status !== 'active') {
      await session.accounts.virtual.activate(account.urn);
      console.log('✓ Account activated');
    }

    return { success: true, account };
  } catch (error) {
    console.error('✗ Setup failed:', error);
    throw error;
  }
}

// Run
setupVirtualAccount('test-user-123')
  .then((result) => console.log('Setup complete:', result))
  .catch((error) => console.error('Setup failed:', error));

Best Practices

  1. Use Sandbox Mode: Always use sandbox mode for virtual accounts
  2. Meaningful Metadata: Add metadata to track test scenarios and purposes
  3. Cleanup: Disable or delete test accounts after use
  4. Error Handling: Always wrap operations in try-catch blocks
  5. Test Isolation: Use unique identifiers to isolate test accounts
  6. Webhooks: Configure webhooks for event notifications during testing
  7. Status Checks: Verify account status before performing operations

Differences from Other Account Types

FeatureVirtual AccountsVirtual CardsBancolombia
KYC RequiredNoYesYes
Creation TimeInstantInstantInstant
Use CaseTesting/DevelopmentPaymentsColombian banking
Required InfoFirst name, Last nameUser URNName (optional)
Reference CodeNoNoYes
Card NumberNoYesNo

Next Steps