npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arminbcbtest/sdk

v0.1.1

Published

The official BCB TypeScript SDK with comprehensive acceptance testing

Readme

BCB Connect - TypeScript SDK

The official TypeScript/JavaScript SDK for BCB Group APIs, designed with the same developer experience principles as Stripe's SDK. This project includes a comprehensive acceptance testing infrastructure that runs against real external servers.

🚀 Quick Start

Installation

npm install @bcb/sdk

Basic Usage

import BCB from '@bcb/sdk';

const bcb = new BCB({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  environment: 'sandbox', // 'sandbox' or 'production'
});

// The SDK handles authentication automatically
// List all accounts
const accounts = await bcb.accounts.list();
console.log(`Found ${accounts.data.length} accounts`);

// Get a specific account
if (accounts.data.length > 0) {
  const account = await bcb.accounts.retrieve(accounts.data[0].id);
  console.log(`Account: ${account.data.account_label}`);
  
  // Get account balance
  const balance = await bcb.balances.get(account.data.id);
  console.log(`Balance: ${balance.data.length} entries`);
  
  // Get recent transactions
  const transactions = await bcb.transactions.list(account.data.id, { limit: 10 });
  console.log(`Recent transactions: ${transactions.data.length}`);
  
  // Make a payment
  const payment = await bcb.payments.createGBPFPS({
    counterparty_id: account.data.counterparty_id,
    sender_account_id: account.data.id,
    beneficiary_name: 'John Smith',
    beneficiary_account_number: '12345678',
    beneficiary_sort_code: '200000',
    amount: '100.50',
    reference: 'Invoice payment',
    reason: 'GDSV' // Purchase/Sale of Goods and Services
  });
  console.log(`Payment created: ${payment.data[0].transactionId}`);
}

🧪 Comprehensive Testing Infrastructure

This SDK includes a robust acceptance testing framework designed to validate functionality against real BCB API servers. The testing infrastructure is built with three distinct layers:

Test Architecture

  • Unit Tests (tests/unit/) - Fast, isolated component testing
  • Integration Tests (tests/integration/) - Real API integration testing
  • Acceptance Tests (tests/acceptance/) - End-to-end scenarios and performance testing

Key Testing Features

Real API Testing - Tests run against actual BCB servers, not mocks
Multi-Environment Support - Development, staging, production configurations
Performance Monitoring - Built-in performance tracking and assertions
Custom Jest Matchers - BCB-specific test assertions
Comprehensive Error Testing - Real error scenario validation
Load Testing - Concurrent request and performance testing
Event System Testing - SDK event emission validation

Running Tests

# Check test environment configuration
npm run test:env:check

# Run all tests
npm test

# Run specific test types
npm run test:unit         # Unit tests only
npm run test:integration  # Integration tests with real API
npm run test:acceptance   # Full acceptance test suite

# Development testing
npm run test:watch        # Watch mode
npm run test:coverage     # Coverage report

Test Configuration

  1. Copy environment template:

    cp env.example .env
  2. Configure for your environment:

    # Required: BCB API credentials
    BCB_CLIENT_ID=your_client_id
    BCB_CLIENT_SECRET=your_client_secret
       
    # Environment configuration (sandbox or production)
    BCB_ENVIRONMENT=sandbox  # sandbox|production
       
    # Optional: Override specific URLs (if not using predefined environments)
    # BCB_BASE_URL=https://api.bcb.group
    # BCB_AUTH_URL=https://auth.bcb.group
    # BCB_CLIENT_API_URL=https://client-api.bcb.group
       
    # Optional configuration
    BCB_TIMEOUT=30000           # Request timeout in ms
    BCB_MAX_RETRIES=3           # Max retry attempts
  3. Verify configuration:

    npm run test:env:check

📖 SDK Documentation

Environment Configuration

The BCB SDK supports multiple environments with predefined URL configurations:

Predefined Environments

import BCB from '@bcb/sdk';

// Sandbox environment (recommended for development and testing)
const sandboxBcb = new BCB({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  environment: 'sandbox', // Uses *.uat.bcb.group URLs
});

// Production environment
const prodBcb = new BCB({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  environment: 'production', // Uses *.bcb.group URLs (default)
});

Environment URLs

| Environment | Auth URL | Base API URL | Client API URL | |-------------|----------|--------------|----------------| | sandbox | https://auth.uat.bcb.group | https://api.uat.bcb.group | https://client-api.uat.bcb.group | | production | https://auth.bcb.group | https://api.bcb.group | https://client-api.bcb.group |

Custom URLs

You can also override specific URLs when needed:

const bcb = new BCB({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  // Custom URLs override environment settings
  baseURL: 'https://custom-api.example.com',
  authURL: 'https://custom-auth.example.com',
  clientApiURL: 'https://custom-client-api.example.com',
});

Multiple API Endpoints

The SDK provides methods to interact with different API endpoints:

// Base API requests (most common)
const response = await bcb.request('/endpoint');

// Auth API requests (handled automatically, but available if needed)
const authResponse = await bcb.authRequest('/oauth/token');

// Client API requests
const clientResponse = await bcb.clientRequest('/client-endpoint');

Configuration

import { BCB } from '@bcb/sdk';

const bcb = new BCB({
  clientId: 'your_client_id',
  clientSecret: 'your_client_secret',
  baseURL: 'https://api.bcb.group', // optional
  timeout: 30000, // optional
  maxRetries: 3, // optional
});

Authentication

The SDK handles OAuth2 client credentials flow automatically:

// Authentication happens automatically on first API call
const response = await bcb.get('/protected-endpoint');

// Manual token management
const token = await bcb.auth.getAccessToken();
const isAuth = bcb.auth.isAuthenticated();
await bcb.auth.refreshToken();

Accounts Management

The SDK provides comprehensive account listing functionality with Stripe-like patterns:

// List all accounts
const accounts = await bcb.accounts.list();
console.log(`Found ${accounts.data.length} accounts`);

// List accounts with pagination
const paginatedAccounts = await bcb.accounts.list({
  limit: 10,
  pageToken: 123
});

// Filter by counterparty
const counterpartyAccounts = await bcb.accounts.listByCounterparty(483);

// Retrieve specific account by ID
const account = await bcb.accounts.retrieve(378);
console.log(`Account: ${account.data.account_label}`);

// Filter by currency (client-side filtering)
const btcAccounts = await bcb.accounts.listByCurrency('BTC');

// Filter by account type (client-side filtering)
const wallets = await bcb.accounts.listByType('Wallet');
const banks = await bcb.accounts.listByType('Bank');
const custodial = await bcb.accounts.listByType('Custodial');

// Advanced filtering with multiple parameters
const filteredAccounts = await bcb.accounts.listByCounterparty(483, {
  limit: 5
});

Balance Management

The SDK provides comprehensive balance functionality following the same Stripe-like patterns:

// Get current balance for an account (using numeric account ID)
const balance = await bcb.balances.get(539);
console.log(`Found ${balance.data.length} balance entries`);

// Get balance using account ID from accounts list
const accounts = await bcb.accounts.list();
const accountId = accounts.data[0].id; // Use the numeric ID
const balance = await bcb.balances.get(accountId);

// Get end-of-day balance for a specific date
const eodBalance = await bcb.balances.getEndOfDay(539, '2023-12-31');

// Using dateTo parameter directly
const dateBalance = await bcb.balances.get(539, { dateTo: '2023-12-31' });

// Retrieve method (alias for get)
const balance = await bcb.balances.retrieve(539);

// Get balances for multiple accounts
const balances = await bcb.balances.getMultiple([539, 540]);

// Filter balance by currency (client-side filtering)
const btcBalance = await bcb.balances.getByCurrency(539, 'BTC');

// Filter balance by account type (client-side filtering)
const walletBalance = await bcb.balances.getByType(539, 'Wallet');
const bankBalance = await bcb.balances.getByType(539, 'Bank');
const custodialBalance = await bcb.balances.getByType(539, 'Custodial');

Balance Object Structure

interface Balance {
  counterparty_id: number;
  counterparty_name: string;
  counterparty_country: string;
  account_id: number;
  cid: string;              // Alpha-numeric counterparty id
  pid: string;              // Alpha-numeric white-label partner id
  partner_id: number;       // Internal numeric white-label partner id
  account_label: string;
  partner_name: string;     // White-label partner name
  account_name: string;     // Account holder name
  node_address: string;
  node_location: string | null;
  ticker: string;           // Asset identifier (e.g. 'BTC', 'USD')
  balance: number;          // Latest balance
  email: string;            // Account holder email address
  aid: string;              // Alpha-numeric account id
  account_type: 'Bank' | 'Custodial' | 'Wallet';  // Account type
  payment_type: string | null;
  description: string;      // Free-text description of account
  bcb_controlled: number;   // Flag is 1 if BCB can operate the account, 0 otherwise
  is_observable: number;
  host_name: string;
  reporting_ccy: string;    // Home currency of client
  iban: string;
  bic: string;
  host_location: string;
  host_hub: string;
  host_country: string;
  host_country_name: string;
  asset_name: string;       // Common name for the asset identified by the ticker
  display_name: string;     // More descriptive name of the asset
  image_url_trans: string;  // Image URL of asset for display purposes
  decimals_price: number;   // Number of decimals to display in the price
  decimals_quantity: number; // Number of decimals to display in the quantity
}

Account Object Structure

interface Account {
  id: number;
  counterparty_id: number;
  aid: string;              // Account identifier
  cid: string;              // Customer identifier
  account_type: 'Wallet' | 'Bank' | 'Custodial';
  ccy: string;              // Currency code
  host_name: string;        // Host institution name
  host_hub: string;         // Host hub location
  host_location: string;    // Host physical location
  node_name: string;        // Node name
  node_address: string;     // Node address
  node_location: string;    // Node location
  node_country: string;     // Node country code
  node_subaddress: string;  // Node subaddress
  iban: string;             // IBAN (for bank accounts)
  account_label: string;    // Human-readable label
  description: string;      // Account description
  bcb_controlled: number;   // BCB controlled flag (0/1)
  settlement_reference: string;
  created_at: string;       // ISO timestamp
  updated_at: string;       // ISO timestamp
  partner_id: number;
  email: string;            // Contact email
  reporting_ccy: string;    // Reporting currency
  pid: string;              // Partner identifier
  partner_name: string;     // Partner name
}

Transaction Management

The SDK provides comprehensive transaction functionality for retrieving transaction history with advanced filtering options:

// List all transactions for an account (using numeric account ID)
const transactions = await bcb.transactions.list(539);
console.log(`Found ${transactions.data.length} transactions`);

// List transactions using account ID from accounts list
const accounts = await bcb.accounts.list();
const accountId = accounts.data[0].id; // Use the numeric ID
const transactions = await bcb.transactions.list(accountId);

// List transactions with pagination
const paginatedTransactions = await bcb.transactions.list(539, {
  limit: 10,
  pageToken: 123
});

// Filter by date range
const dateRangeTransactions = await bcb.transactions.listByDateRange(
  539, 
  '2023-01-01', 
  '2023-12-31'
);

// Using date parameters directly
const filteredTransactions = await bcb.transactions.list(539, {
  dateFrom: '2023-01-01',
  dateTo: '2023-12-31',
  limit: 50
});

// Filter by transaction type (client-side filtering)
const creditTransactions = await bcb.transactions.listCredits(539);
const debitTransactions = await bcb.transactions.listDebits(539);

// Get transactions for multiple accounts
const multipleTransactions = await bcb.transactions.listMultiple([539, 540]);

// Alias method for consistency
const transactions = await bcb.transactions.listForAccount(539);

// Advanced filtering with multiple parameters
const advancedTransactions = await bcb.transactions.list(539, {
  dateFrom: '2023-01-01',
  dateTo: '2023-12-31',
  limit: 20
});

// Get specific transaction detail by ID
const transactionDetail = await bcb.transactions.retrieveDetail(539, '7b73263d-65dc-11e9-b39c-42010a8400a9');
console.log(`Transaction: ${transactionDetail.data.tx_id}`);
console.log(`Amount: ${transactionDetail.data.amount} ${transactionDetail.data.ticker}`);
console.log(`Network: ${transactionDetail.data.network}`);
console.log(`Type: ${transactionDetail.data.credit ? 'Credit' : 'Debit'}`);
console.log(`Status: ${transactionDetail.data.approved ? 'Approved' : 'Pending'}`);

// Access detailed bank information
if (transactionDetail.data.details.iban) {
  console.log(`IBAN: ${transactionDetail.data.details.iban}`);
  console.log(`Account Name: ${transactionDetail.data.details.account_name}`);
  console.log(`Reference: ${transactionDetail.data.details.reference}`);
}

// Retrieve alias method (same as retrieveDetail)
const transaction = await bcb.transactions.retrieve(539, '7b73263d-65dc-11e9-b39c-42010a8400a9');

Transaction Object Structure

interface Transaction {
  tx_id: string;                    // Unique transaction ID
  account_id: number;               // Internal numeric account ID
  blinc_id?: string | null;         // BLINC network ID (if applicable)
  network?: string;                 // Payment network (e.g., 'FPS', 'Bitcoin')
  value_date: string;               // Transaction date (ISO timestamp)
  credit: number;                   // 1 for credit, 0 for debit
  details: TransactionDetails;      // Transaction details object
  ticker: string;                   // Asset identifier (e.g., 'BTC', 'GBP', 'USD')
  amount: number;                   // Transaction amount
  approved: number;                 // 1 for approved, 0 for pending
  notes?: string | null;            // Optional transaction notes
  source_name?: string;             // Source system name (e.g., 'clearbank')
}

interface TransactionDetails {
  bic?: string | null;              // Bank Identifier Code
  iban?: string | null;             // International Bank Account Number
  blinc_id?: string | null;         // BLINC network identifier
  reference?: string;               // Payment reference
  sort_code?: string;               // Bank sort code
  account_name?: string;            // Account holder name
  account_number?: string;          // Account number
  counterparty_reference?: string;  // Counterparty reference
  endToEndIdentifier?: string;      // End-to-end identifier
  nonce?: number;                   // Unique nonce value
}

HTTP Methods

// GET request
const users = await bcb.get('/users');

// POST request
const newUser = await bcb.post('/users', {
  name: 'John Doe',
  email: '[email protected]',
});

// PUT, PATCH, DELETE
const updated = await bcb.put('/users/123', userData);
const patched = await bcb.patch('/users/123', { name: 'Jane' });
await bcb.delete('/users/123');

Error Handling

import { 
  BCBAuthenticationError,
  BCBRateLimitError,
  BCBAPIError 
} from '@bcb/sdk';

try {
  const response = await bcb.get('/endpoint');
} catch (error) {
  if (error instanceof BCBAuthenticationError) {
    console.error('Auth failed:', error.message);
  } else if (error instanceof BCBRateLimitError) {
    console.error('Rate limited:', error.headers?.['retry-after']);
  }
}

Beneficiaries Management

The SDK provides comprehensive beneficiary management functionality following the same Stripe-like patterns:

// List all beneficiaries
const beneficiaries = await bcb.beneficiaries.list();
console.log(`Found ${beneficiaries.data.length} beneficiaries`);

// List beneficiaries with pagination
const paginatedBeneficiaries = await bcb.beneficiaries.list({
  limit: 10,
  pageToken: 123
});

// Retrieve specific beneficiary by ID
const beneficiary = await bcb.beneficiaries.retrieve(378);
console.log(`Beneficiary: ${beneficiary.data.account_label}`);

// Filter by currency (client-side filtering)
const btcBeneficiaries = await bcb.beneficiaries.listByCurrency('BTC');

// Filter by account type (client-side filtering)
const wallets = await bcb.beneficiaries.listByType('Wallet');
const banks = await bcb.beneficiaries.listByType('Bank');
const custodial = await bcb.beneficiaries.listByType('Custodial');

// Filter by counterparty (client-side filtering)
const counterpartyBeneficiaries = await bcb.beneficiaries.listByCounterparty(483);

// Advanced filtering with multiple parameters
const filteredBeneficiaries = await bcb.beneficiaries.listByCurrency('BTC', {
  limit: 5
});

Beneficiary Object Structure

interface Beneficiary {
  id: number;                       // Numeric internal beneficiary ID
  counterparty_id: number;          // Numeric internal counterparty ID
  aid: string;                      // Alpha-numeric beneficiary ID
  cid: string;                      // Alpha-numeric counterparty ID
  account_type: 'Wallet' | 'Bank' | 'Custodial';  // Account type
  ccy: string;                      // Asset ticker, or left hand side of trading symbol
  host_name: string;                // Bank Name for bank accounts, Custodian Name for custody accounts, Wallet Name for wallet accounts
  host_hub: string;                 // Branch Code for bank accounts, optional for custody and wallets
  host_location: string;            // Bank Address for bank accounts, optional for custody and wallets
  node_name: string;                // Account holder's legal name
  node_address: string;             // Account Number for bank accounts, wallet address for wallets, optional for custody
  node_location: string;            // Account holder's residential address for bank accounts, optional memo field for custody and wallets
  node_country: string;             // Account holder's residential address country code (2-letter ISO country code), optional memo field for custody and wallets
  node_subaddress?: string;         // Optional sub-address for wallets
  iban: string;                     // IBAN for bank accounts
  account_label: string;            // Display name for beneficiary
  description: string;              // Free-text description of beneficiary
  settlement_reference: string;     // Optional settlement reference for payments
  created_at: string;               // Timestamp of account record creation (YYYY-MM-DDTHH:MM:SS.000Z)
  updated_at: string;               // Timestamp of account record update (YYYY-MM-DDTHH:MM:SS.000Z)
  partner_id: number;               // Internal numeric ID of white-label partner
  email: string;                    // Email address of beneficiary
  pid: string;                      // Alpha-numeric ID of white-label partner
  partner_name: string;             // White-label partner name
}

Beneficiary Usage Examples

// Display beneficiary details
const beneficiary = await bcb.beneficiaries.retrieve(378);
console.log('Beneficiary Details:');
console.log(`  Label: ${beneficiary.data.account_label}`);
console.log(`  Type: ${beneficiary.data.account_type}`);
console.log(`  Currency: ${beneficiary.data.ccy}`);
console.log(`  Host: ${beneficiary.data.host_name}`);
console.log(`  Partner: ${beneficiary.data.partner_name}`);

if (beneficiary.data.iban) {
  console.log(`  IBAN: ${beneficiary.data.iban}`);
}

if (beneficiary.data.node_address) {
  console.log(`  Address: ${beneficiary.data.node_address}`);
}

// Filter and analyze beneficiaries
const allBeneficiaries = await bcb.beneficiaries.list();

// Group by currency
const byCurrency = allBeneficiaries.data.reduce((acc, b) => {
  acc[b.ccy] = (acc[b.ccy] || 0) + 1;
  return acc;
}, {} as Record<string, number>);

console.log('Beneficiaries by currency:', byCurrency);

// Group by account type
const byType = allBeneficiaries.data.reduce((acc, b) => {
  acc[b.account_type] = (acc[b.account_type] || 0) + 1;
  return acc;
}, {} as Record<string, number>);

console.log('Beneficiaries by type:', byType);

Payments Management (V5 API)

The SDK provides comprehensive payment functionality using the latest V5 payments endpoint with enhanced address validation and support for multiple payment schemes and currencies.

Purpose Code Validation

All payments require a valid reason code (purpose code) that complies with banking standards. The SDK validates these codes before making API calls to provide immediate feedback:

// ✅ Valid reason codes (examples)
'BKFE' // Bank loan fees
'GDSV' // Purchase/Sale of Goods and Services  
'SALA' // Salary Payment
'RENT' // Rent payment
'TAXS' // Tax Payment
'SERV' // Service charges
// ... and 100+ other valid codes

// ❌ Invalid reason codes will throw an error
reason: 'invalid-code' // Error: Invalid reason code 'invalid-code'. Must be one of the following valid purpose codes: BKFE, BKIP, BKPP, ...

Purpose Code Categories:

  • Bank Debt: BKFE, BKIP, BKPP
  • Commercial: GDSV, SERV, SUPP, TRAD, etc.
  • Salary & Benefits: SALA, BONU, COMM, PENS, etc.
  • Tax: TAXS, INTX, VATX, HSTX, etc.
  • Utilities: ELEC, GASB, WATR, PHON, etc.
  • And many more categories...
// GBP FPS Payment (Faster Payments)
const fpsPayment = await bcb.payments.createGBPFPS({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_name: 'John Smith',
  beneficiary_account_number: '12345678',
  beneficiary_sort_code: '200000',
  amount: '100.50',
  reference: 'Invoice INV-2024-001',
  reason: 'GDSV', // Purchase/Sale of Goods and Services
  notes: 'Urgent payment'
});

// GBP CHAPS Payment (High Value)
const chapsPayment = await bcb.payments.createGBPCHAPS({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_name: 'Property Holdings Ltd',
  beneficiary_account_number: '87654321',
  beneficiary_sort_code: '400000',
  beneficiary_address_line_1: '1 Property Square',
  beneficiary_city: 'London',
  beneficiary_postcode: 'E14 5AB', // Mandatory for CHAPS
  beneficiary_country: 'GB',
  beneficiary_lei_code: 'ABCDEFGHIJKLMNOP1234', // Mandatory for CHAPS
  amount: '1500000.00',
  reference: 'Property Purchase',
  reason: 'HLRP' // Property Loan
});

// USD Payment
const usdPayment = await bcb.payments.createUSD({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_name: 'Tech Solutions Inc',
  beneficiary_account_number: '123456789',
  beneficiary_routing_number: '021000021',
  beneficiary_address_line_1: '123 Silicon Valley Drive',
  beneficiary_city: 'San Francisco',
  beneficiary_country: 'US',
  beneficiary_bank_name: 'Chase Bank',
  beneficiary_bank_address: '270 Park Avenue, New York, NY 10017',
  beneficiary_bank_country: 'US',
  amount: '2500.00',
  reference: 'Software License',
  reason: 'SERV' // Service charges
});

// EUR Payment with IBAN
const eurPayment = await bcb.payments.createEUR({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_name: 'Deutsche Manufacturing GmbH',
  beneficiary_address_line_1: 'Industriestraße 45',
  beneficiary_city: 'Munich',
  beneficiary_country: 'DE',
  beneficiary_bic: 'DEUTDEFF',
  beneficiary_iban: 'DE89370400440532013000',
  amount: '750.00',
  reference: 'Equipment Purchase',
  reason: 'GDSV' // Purchase/Sale of Goods and Services
});

// International Payment (any currency)
const chfPayment = await bcb.payments.createInternational('CHF', {
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_name: 'Swiss Precision AG',
  beneficiary_address_line_1: 'Bahnhofstrasse 123',
  beneficiary_city: 'Zurich',
  beneficiary_country: 'CH',
  beneficiary_bic: 'UBSWCHZH80A',
  beneficiary_account_number: 'CH9300762011623852957',
  amount: '500.00',
  reference: 'Consulting Services',
  reason: 'SERV' // Service charges
});

// Payment with existing beneficiary
const beneficiaryPayment = await bcb.payments.authorize({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_account_id: 456,
  ccy: 'GBP',
  amount: '75.00',
  reference: 'Monthly Payment',
  reason: 'SERV', // Service charges
  preferred_scheme: 'AUTO',
  // V5 mandatory address fields
  beneficiary_address_line_1: '123 Stored Address',
  beneficiary_city: 'London',
  beneficiary_country: 'GB'
});

// BLINC Network Payment
const blincPayment = await bcb.payments.createBLINC({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_account_id: 456,
  ccy: 'BTC',
  amount: '0.001',
  reference: 'Crypto Transfer',
  reason: 'TRAD', // Commercial transaction
  beneficiary_address_line_1: 'Digital Network',
  beneficiary_city: 'BLINC Network',
  beneficiary_country: 'XX'
});

// Internal Transfer
const internalPayment = await bcb.payments.createInternal({
  counterparty_id: 483,
  sender_account_id: 378,
  beneficiary_account_id: 379,
  ccy: 'USD',
  amount: '1000.00',
  reference: 'Internal Transfer',
  reason: 'ACCTCASH', // Account Management
  beneficiary_address_line_1: 'Internal',
  beneficiary_city: 'Same Institution',
  beneficiary_country: 'XX'
});

Payment Schemes

The V5 payments API supports three payment schemes:

  • AUTO - Automatic scheme selection for external payments
  • BLINC - Payments within the BLINC network (requires BLINC accounts)
  • INTERNAL - Payments between BCB accounts of the same counterparty

Currency-Specific Requirements

The SDK automatically validates currency-specific requirements:

GBP Payments:

  • FPS: Account number + sort code (under £1M)
  • CHAPS: Account number + sort code + full address + postcode + LEI code (over £1M)
  • SWIFT: Account number + sort code + SWIFT code (international)

USD Payments:

  • Account number + routing number + bank name + bank address

EUR Payments:

  • BIC + IBAN (or account number for non-IBAN countries)

Other Currencies:

  • BIC + account number + full address

V5 Address Fields (Mandatory)

All V5 payments require enhanced address fields:

  • beneficiary_address_line_1 (mandatory)
  • beneficiary_address_line_2 (optional)
  • beneficiary_address_line_3 (optional)
  • beneficiary_city (mandatory)
  • beneficiary_region (optional)
  • beneficiary_postcode (mandatory for CHAPS)
  • beneficiary_country (mandatory)
  • beneficiary_lei_code (mandatory for CHAPS)

Payment Response

interface PaymentResponse {
  endToEndIdentification: string;  // Payment end-to-end ID
  transactionId: string;           // BCB transaction ID
  nonce?: string;                  // Optional nonce (if provided)
  response: 'Accepted' | 'Rejected'; // Payment status
}

Purpose Codes

The SDK supports all standard purpose codes for payment categorization. Common examples:

  • GDSV - Purchase Sale of Goods and Services
  • SALA - Salary Payment
  • RENT - Rent Payment
  • INSU - Insurance Premium
  • TAXS - Tax Payment
  • DIVD - Dividends

Validation and Error Handling

The SDK provides comprehensive validation:

  • Required field validation
  • Currency-specific field validation
  • Amount format validation (decimal strings)
  • CHAPS-specific requirements (postcode, LEI code)
  • Scheme-specific requirements (BLINC, INTERNAL)
try {
  const payment = await bcb.payments.authorize(paymentRequest);
  console.log(`Payment ${payment.data[0].response}: ${payment.data[0].transactionId}`);
} catch (error) {
  if (error.message.includes('Amount must be')) {
    console.error('Invalid amount format');
  } else if (error.message.includes('CHAPS payments require')) {
    console.error('Missing CHAPS requirements');
  } else {
    console.error('Payment failed:', error.message);
  }
}

BLINC Network Management

The SDK provides comprehensive BLINC (Blockchain Interoperability Network for Commerce) functionality for managing connected BLINC members and beneficiaries:

// List BLINC beneficiaries for a specific account
const blincBeneficiaries = await bcb.blinc.listBeneficiaries(9999);
console.log(`Found ${blincBeneficiaries.data.length} BLINC beneficiaries`);

// Filter BLINC beneficiaries by currency
const gbpBlincBeneficiaries = await bcb.blinc.listBeneficiariesByCurrency(9999, 'GBP');
const eurBlincBeneficiaries = await bcb.blinc.listBeneficiaries(9999, { ccy: 'EUR' });

// Get a BLINC member by BLINC ID
const blincMember = await bcb.blinc.getMember('800999999999');
console.log(`Member: ${blincMember.data[0].name}`);

// Get single BLINC member (convenience method)
const singleMember = await bcb.blinc.getMemberSingle('800999999999');
console.log(`Member: ${singleMember.data.name} (${singleMember.data.ccy})`);

// Check if a BLINC member exists
const memberExists = await bcb.blinc.memberExists('800999999999');
if (memberExists) {
  console.log('BLINC member found');
}

// Get available currencies for BLINC beneficiaries
const currencies = await bcb.blinc.getAvailableCurrencies(9999);
console.log(`Available currencies: ${currencies.join(', ')}`);

// Search BLINC beneficiaries by name
const searchResults = await bcb.blinc.searchBeneficiariesByName(9999, 'John');
const filteredSearch = await bcb.blinc.searchBeneficiariesByName(9999, 'Smith', { ccy: 'GBP' });

BLINC Beneficiary Object Structure

interface BlincBeneficiary {
  id: number;                    // Numeric internal account ID
  blinc_id: string;              // Numeric BLINC account ID
  ccy: string;                   // Currency of the BLINC account
  name: string;                  // Name of the BLINC account holder
}

BLINC Member Object Structure

interface BlincMember {
  id: number;                    // Numeric internal account ID
  blinc_id: string;              // Numeric BLINC account ID
  ccy: string;                   // Currency of the BLINC account
  name: string;                  // Name of the BLINC account holder
}

BLINC Usage Examples

// Get all BLINC beneficiaries for account analysis
const accounts = await bcb.accounts.list();
const firstAccountId = accounts.data[0].id;

const allBlincBeneficiaries = await bcb.blinc.listBeneficiaries(firstAccountId);
console.log(`Total BLINC beneficiaries: ${allBlincBeneficiaries.data.length}`);

// Group BLINC beneficiaries by currency
const byCurrency = allBlincBeneficiaries.data.reduce((acc, b) => {
  acc[b.ccy] = (acc[b.ccy] || 0) + 1;
  return acc;
}, {} as Record<string, number>);

console.log('BLINC beneficiaries by currency:', byCurrency);

// Find specific BLINC members
for (const beneficiary of allBlincBeneficiaries.data) {
  const memberDetail = await bcb.blinc.getMemberSingle(beneficiary.blinc_id);
  console.log(`${memberDetail.data.name}: ${memberDetail.data.ccy}`);
}

// Currency-specific BLINC operations
const availableCurrencies = await bcb.blinc.getAvailableCurrencies(firstAccountId);

for (const currency of availableCurrencies) {
  const currencyBeneficiaries = await bcb.blinc.listBeneficiariesByCurrency(firstAccountId, currency);
  console.log(`${currency}: ${currencyBeneficiaries.data.length} BLINC beneficiaries`);
}

// Search and filter operations
const searchTerm = 'BLINC';
const searchResults = await bcb.blinc.searchBeneficiariesByName(firstAccountId, searchTerm);
console.log(`Found ${searchResults.data.length} BLINC beneficiaries matching "${searchTerm}"`);

// Combined search with currency filter
const gbpSearchResults = await bcb.blinc.searchBeneficiariesByName(
  firstAccountId, 
  'Member', 
  { ccy: 'GBP' }
);
console.log(`GBP BLINC members containing "Member": ${gbpSearchResults.data.length}`);

BLINC Network Features

The BLINC functionality provides:

  • Connected Members: List all BLINC accounts connected to your entity
  • Currency Filtering: Filter BLINC beneficiaries by specific currencies
  • Member Lookup: Get detailed information about specific BLINC members
  • Existence Checking: Verify if a BLINC member exists before operations
  • Name Search: Search BLINC beneficiaries by name with partial matching
  • Currency Discovery: Get all available currencies for BLINC beneficiaries

Note: All BLINC accounts connected to your entity are returned regardless of which specific account ID was requested. The BLINC network provides interoperability between all connected members.

Beneficiary Creation (V4 API)

The SDK provides comprehensive beneficiary creation functionality using the V4 accounts endpoint. The beneficiaries service now includes both listing and creation methods for a complete beneficiary management solution:

// Create a GBP beneficiary with Account Number + Sort Code
const gbpBeneficiary = await bcb.beneficiaries.createGBP({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "John Smith",
  host_name: "Barclays Bank",
  sort_code: "200000",
  account_number: "12345678",
  node_name: "John Smith",
  node_location_line_1: "123 Main Street",
  node_location_city: "London",
  node_location_postcode: "SW1A 1AA",
  node_type: "individual",
  email: "[email protected]"
});

// Create a USD beneficiary with routing number
const usdBeneficiary = await bcb.beneficiaries.createUSD({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "Jane Doe",
  host_name: "Bank of America",
  routing_number: "121000358",
  account_number: "987654321",
  node_name: "Jane Doe",
  node_location_line_1: "456 Wall Street",
  node_location_city: "New York",
  node_location_postcode: "10005",
  node_type: "individual",
  email: "[email protected]"
});

// Create an IBAN beneficiary (EUR and other IBAN countries)
const eurBeneficiary = await bcb.beneficiaries.createIBAN({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "Marie Dubois",
  ccy: "EUR",
  host_name: "BNP Paribas",
  host_location: "Paris, France",
  host_country: "FR",
  node_name: "Marie Dubois",
  node_location_line_1: "456 Rue de Rivoli",
  node_location_city: "Paris",
  node_location_postcode: "75001",
  node_country: "FR",
  node_type: "individual",
  iban: "FR1420041010050500013M02606", // Valid French IBAN
  bic: "BNPAFRPPXXX"
});

// Create a cryptocurrency wallet beneficiary
const btcBeneficiary = await bcb.beneficiaries.createWallet({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "Crypto Wallet",
  ccy: "BTC",
  node_name: "Bitcoin Wallet",
  wallet_address: "3LxDswEWKC3mJewRYs44xr3qNBqXMwhszo",
  node_location_line_1: "789 Tech Street",
  node_location_city: "San Francisco",
  node_location_postcode: "94105",
  node_country: "US",
  node_type: "individual"
});

// Generic beneficiary creation with full control
const customBeneficiary = await bcb.beneficiaries.create({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "Custom Beneficiary",
  account_type: "Bank",
  ccy: "CAD",
  host_name: "Royal Bank of Canada",
  host_hub: "003200233", // Canadian routing number
  host_country: "CA",
  node_name: "Custom User",
  node_address: "123456789",
  node_location_line_1: "123 Maple Street",
  node_location_city: "Toronto",
  node_location_postcode: "M5V 3A8",
  node_country: "CA",
  node_type: "individual",
  bic: "ROYCCAT2XXX",
  account_label: "Custom User - CAD",
  email: "[email protected]",
  bcb_controlled: 1,
  is_beneficiary: 1
});

Beneficiary Creation Convenience Methods

The beneficiaries service provides country and currency-specific convenience methods:

  • bcb.beneficiaries.createGBP() - For UK bank accounts with sort codes
  • bcb.beneficiaries.createUSD() - For US bank accounts with routing numbers
  • bcb.beneficiaries.createIBAN() - For IBAN-based bank accounts (EUR and other IBAN countries)
  • bcb.beneficiaries.createWallet() - For cryptocurrency wallet addresses
  • bcb.beneficiaries.create() - Generic method with full control over all parameters

Validation and Requirements

The SDK automatically validates beneficiary creation requests based on:

  • Account Type: Bank accounts require either IBAN or account number
  • Currency Requirements: USD in US requires routing number, GBP in UK requires sort code
  • Address Requirements: GBP accounts require full address for CHAPS payments
  • Node Type: Must be either "individual" or "corporate"
  • Required Flags: bcb_controlled and is_beneficiary must be 1

Create Beneficiary Response

interface CreateBeneficiaryResponse {
  id: number;  // Unique beneficiary ID of the created account
}

Note: The V4 accounts endpoint returns only the ID of the created beneficiary account. To retrieve the full beneficiary details, use the beneficiaries list endpoint with the returned ID.

Country-Specific Examples

// UK GBP Account with Sort Code
const ukAccount = await bcb.beneficiaries.createGBP({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "UK Beneficiary",
  host_name: "HSBC",
  sort_code: "400000", // UK sort code
  account_number: "12345678",
  node_name: "John Smith",
  node_location_line_1: "123 High Street",
  node_location_city: "London",
  node_location_postcode: "SW1A 1AA", // Required for CHAPS
  node_type: "individual"
});

// US USD Account with Routing Number
const usAccount = await bcb.beneficiaries.createUSD({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "US Beneficiary",
  host_name: "Chase Bank",
  routing_number: "021000021", // US routing number
  account_number: "123456789",
  node_name: "Jane Doe",
  node_location_line_1: "456 Broadway",
  node_location_city: "New York",
  node_location_postcode: "10013",
  node_type: "individual"
});

// EU EUR Account with IBAN
const euAccount = await bcb.beneficiaries.createIBAN({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "EU Beneficiary",
  ccy: "EUR",
  host_name: "Deutsche Bank",
  host_country: "DE",
  node_name: "Hans Mueller",
  node_location_line_1: "Hauptstraße 123",
  node_location_city: "Frankfurt",
  node_location_postcode: "60311",
  node_country: "DE",
  node_type: "individual",
  iban: "DE89370400440532013000", // German IBAN
  bic: "DEUTDEFFXXX"
});

// Bitcoin Wallet
const btcWallet = await bcb.beneficiaries.createWallet({
  counterparty_id: 483,
  cid: "CX00000101",
  name: "BTC Wallet",
  ccy: "BTC",
  node_name: "Bitcoin Wallet",
  wallet_address: "3LxDswEWKC3mJewRYs44xr3qNBqXMwhszo",
  node_location_line_1: "789 Crypto Street",
  node_location_city: "San Francisco",
  node_location_postcode: "94105",
  node_country: "US",
  node_type: "individual"
});

VOP (Verification of Payee) Management

The SDK provides comprehensive VOP functionality for approving and canceling payments where the beneficiary name does not match the name on the account. This is essential for compliance with banking regulations and fraud prevention.

Note: VOP is part of the payments module and uses the Client API endpoint (client-api.bcb.group) rather than the standard API endpoint.

// Approve a payment for verification of payee
const approveResult = await bcb.payments.vop.approve(123, 'E2E123456789');
console.log(`Payment approved: ${approveResult.status}`); // 202 - Accepted

// Cancel a payment for verification of payee
const cancelResult = await bcb.payments.vop.cancel(123, 'E2E987654321');
console.log(`Payment cancelled: ${cancelResult.status}`); // 202 - Accepted

VOP Methods

// Approve unverified payment
const approveResult = await bcb.payments.vop.approve(accountId, e2eId);

// Cancel unverified payment
const cancelResult = await bcb.payments.vop.cancel(accountId, e2eId);

VOP Response Codes

The VOP endpoints return specific HTTP status codes:

  • 202 - Success (payment approved/cancelled)
  • 401 - Unauthorized (invalid credentials)
  • 404 - No unverified payment found for the account with the given end-to-end ID
  • 409 - The payment is not unverified (already processed)

Error Handling

try {
  const result = await bcb.payments.vop.approve(123, 'E2E123456789');
  console.log('Payment approved successfully');
} catch (error) {
  if (error.message.includes('404')) {
    console.log('No unverified payment found for this account with the given end-to-end ID');
  } else if (error.message.includes('409')) {
    console.log('The payment is not unverified');
  } else if (error.message.includes('401')) {
    console.log('Unauthorized - check your credentials');
  } else {
    console.error('VOP operation failed:', error.message);
  }
}

Batch VOP Operations

// Process multiple VOP approvals
const payments = [
  { accountId: 123, e2eId: 'E2E111111111' },
  { accountId: 123, e2eId: 'E2E222222222' },
  { accountId: 456, e2eId: 'E2E333333333' },
];

const results = await Promise.allSettled(
  payments.map(payment => 
    bcb.payments.vop.approve(payment.accountId, payment.e2eId)
  )
);

results.forEach((result, index) => {
  const payment = payments[index];
  if (result.status === 'fulfilled') {
    console.log(`✅ Payment ${payment.e2eId} approved successfully`);
  } else {
    console.log(`❌ Payment ${payment.e2eId} failed:`, result.reason.message);
  }
});

VOP with Retry Logic

const retryApprove = async (accountId: number, e2eId: string, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const result = await bcb.payments.vop.approve(accountId, e2eId);
      console.log(`✅ Payment approved on attempt ${attempt}`);
      return result;
    } catch (error) {
      console.log(`❌ Attempt ${attempt} failed:`, error.message);
      
      if (attempt === maxRetries) {
        throw error;
      }
      
      // Wait before retry (exponential backoff)
      const delay = Math.pow(2, attempt) * 1000;
      console.log(`⏳ Waiting ${delay}ms before retry...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
};

try {
  await retryApprove(123, 'E2E444444444');
} catch (error) {
  console.log('❌ All retry attempts failed:', error.message);
}

Virtual Accounts Management (V2 Client API)

The SDK provides comprehensive virtual accounts functionality for creating virtual accounts for individuals and companies with different currency requirements (EUR with IBAN/BIC, GBP with sort code/account number).

Note: Virtual accounts use the Client API endpoint (client-api.bcb.group) rather than the standard API endpoint.

// Create mixed EUR and GBP virtual accounts
const mixedOwners = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440001", // UUID format required
    name: "Hans Mueller",
    addressLine1: "Friedrichstrasse",
    addressLine2: "100",
    city: "Berlin",
    region: "Berlin",
    postcode: "10115",
    country: "DE",
    nationality: "DE",
    dateOfBirth: "1985-03-15",
    isIndividual: true,
    iban: "DE53202208000099106929",
    bicSwift: "MHSBDEHBXXX"
  },
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440002", // UUID format required
    name: "ACME Trading Ltd",
    addressLine1: "One London Wall",
    addressLine2: "6th Floor",
    city: "London",
    region: "Greater London",
    postcode: "EC2Y 5EB",
    country: "GB",
    nationality: "GB",
    registrationNumber: "AB123456",
    isIndividual: false,
    accountNumber: "55555555",
    sortCode: "202015"
  }
];

const segregatedAccountId = 123; // Your segregated account ID
const response = await bcb.virtualAccounts.create(segregatedAccountId, {
  owners: mixedOwners
});

console.log(`Status: ${response.status}`); // 202 - Accepted
console.log(`Response: ${JSON.stringify(response.data)}`);

Convenience Methods

The virtual accounts service provides convenient methods for specific use cases:

// Create virtual accounts for individuals only
const individuals = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440003", // UUID format required
    name: "Alice Johnson",
    addressLine1: "123 High Street",
    city: "Manchester",
    postcode: "M1 1AA",
    country: "GB",
    nationality: "GB",
    dateOfBirth: "1988-11-03",
    accountNumber: "12345678",
    sortCode: "123456"
  }
];

const individualResponse = await bcb.virtualAccounts.createForIndividuals(
  segregatedAccountId,
  individuals
);

// Create virtual accounts for companies only
const companies = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440004", // UUID format required
    name: "Digital Solutions Ltd",
    addressLine1: "Tech Park",
    addressLine2: "Building A",
    city: "Cambridge",
    postcode: "CB1 2AB",
    country: "GB",
    nationality: "GB",
    registrationNumber: "12345678",
    accountNumber: "87654321",
    sortCode: "654321"
  }
];

const companyResponse = await bcb.virtualAccounts.createForCompanies(
  segregatedAccountId,
  companies
);

// Create virtual accounts without bank details (receive-only)
const noBankDetails = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440005", // UUID format required
    name: "Startup Company Ltd",
    addressLine1: "Innovation Hub",
    city: "Bristol",
    postcode: "BS1 1AA",
    country: "GB",
    nationality: "GB",
    registrationNumber: "STARTUP123",
    isIndividual: false
  }
];

const noBankResponse = await bcb.virtualAccounts.createWithoutBankDetails(
  segregatedAccountId,
  noBankDetails
);

// Currency-specific convenience methods
const gbpOwners = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440006", // UUID format required
    name: "GBP Account Holder",
    addressLine1: "London Street",
    city: "London",
    postcode: "SW1A 1AA",
    country: "GB",
    nationality: "GB",
    dateOfBirth: "1990-01-01",
    isIndividual: true,
    accountNumber: "11111111",
    sortCode: "111111"
  }
];

const gbpResponse = await bcb.virtualAccounts.createGBP(segregatedAccountId, gbpOwners);

const eurOwners = [
  {
    correlationId: "550e8400-e29b-41d4-a716-446655440007", // UUID format required
    name: "EUR Account Holder",
    addressLine1: "Berlin Strasse",
    city: "Berlin",
    postcode: "10115",
    country: "DE",
    nationality: "DE",
    registrationNumber: "DE123456",
    isIndividual: false,
    iban: "DE89370400440532013000",
    bicSwift: "COBADEFFXXX"
  }
];

const eurResponse = await bcb.virtualAccounts.createEUR(segregatedAccountId, eurOwners);

Virtual Account Owner Object Structure

interface VirtualAccountOwner {
  correlationId: string;          // Unique identifier for this owner (must be valid UUID format)
  name: string;                   // Legal name of the account holder
  addressLine1: string;           // Primary address line (mandatory)
  addressLine2?: string;          // Secondary address line (optional)
  addressLine3?: string;          // Tertiary address line (optional)
  city: string;                   // City (mandatory)
  region?: string;                // Region/state (optional)
  postcode: string;               // Postal/ZIP code (mandatory)
  country: string;                // 2-letter country code (mandatory)
  nationality: string;            // 2-letter nationality code (mandatory)
  isIndividual: boolean;          // true for individuals, false for companies
  
  // For individuals only
  dateOfBirth?: string;           // YYYY-MM-DD format (required for individuals)
  
  // For companies only
  registrationNumber?: string;    // Company registration number (required for companies)
  
  // Bank details (optional - can create without for receive-only accounts)
  iban?: string;                  // International Bank Account Number
  bicSwift?: string;              // Bank Identifier Code / SWIFT code
  
  // GBP specific fields
  accountNumber?: string;         // 8-digit UK account number
  sortCode?: string;              // 6-digit UK sort code
}

Virtual Accounts Features

  • Mixed Owner Types: Create accounts for both individuals and companies in a single request
  • Currency Support: EUR (IBAN/BIC) and GBP (sort code/account number) with automatic validation
  • Receive-Only Accounts: Create accounts without bank details for incoming payments only
  • Bulk Creation: Up to 1000 owners per request for efficient bulk operations
  • Account Listing: List and search virtual accounts with pagination support
  • Account Closure: Close virtual accounts by IBAN when no longer needed
  • Complete Lifecycle: Create → List → Find virtual IBAN → Close workflow
  • Comprehensive Validation: Client-side validation of all required fields and formats
  • Convenience Methods: Currency and type-specific methods for common use cases

Requirements and Validation

Individual Accounts:

  • dateOfBirth is required (YYYY-MM-DD format)
  • registrationNumber should not be provided

Company Accounts:

  • registrationNumber is required
  • dateOfBirth should not be provided

GBP Bank Details:

  • accountNumber must be 8 digits
  • sortCode must be 6 digits
  • Both are required together or both omitted

EUR Bank Details:

  • iban must be valid IBAN format (2 letters + 2 digits + up to 30 alphanumeric)
  • bicSwift must be valid BIC format (8 or 11 characters)

Address Requirements:

  • country and nationality must be 2-letter ISO country codes
  • addressLine1, city, and postcode are mandatory
  • addressLine2, addressLine3, and region are optional

Error Handling

The SDK provides comprehensive validation before making API calls:

try {
  const response = await bcb.virtualAccounts.create(accountId, { owners });
  console.log(`Created ${owners.length} virtual accounts successfully`);
} catch (error) {
  if (error.message.includes('correlationId is required')) {
    console.error('Missing correlation ID');
  } else if (error.message.includes('Maximum 1000 owners allowed')) {
    console.error('Too many owners in request');
  } else if (error.message.includes('dateOfBirth is required for individuals')) {
    console.error('Individual missing date of birth');
  } else if (error.status === 404) {
    console.error('Segregated account not found');
  } else {
    console.error('Virtual account creation failed:', error.message);
  }
}

// Close a virtual account
try {
  const closeResponse = await bcb.virtualAccounts.close(accountId, "GB33BUKB20201555555555");
  console.log('Virtual account closed successfully');
} catch (error) {
  if (error.status === 404) {
    console.error('Virtual account or IBAN not found');
  } else {
    console.error('Virtual account closure failed:', error.message);
  }
}

Event Monitoring

// Monitor requests and responses
bcb.on('request', ({ request }) => {
  console.log('Request:', request.method, request.url);
});

bcb.on('response', ({ response }) => {
  console.log('Response:', response.status, response.requestId);
});

bcb.on('error', ({ error }) => {
  console.error('Error:', error.message);
});

🔧 Development

Building

npm run build       # Build both CJS and ESM
npm run build:cjs   # CommonJS build
npm run build:esm   # ES Modules build

Code Quality

npm run lint        # ESLint
npm run lint:fix    # Fix linting issues
npm run format      # Prettier formatting
npm run typecheck       # TypeScript checking for src/ (also runs in CI before build)
npm run typecheck:test  # TypeScript checking for tests/ + src/

Testing Development

The testing infrastructure is designed to be highly expandable:

Adding New Tests

// tests/acceptance/my-feature.test.ts
import { createTestContext, skipIfConfigInvalid } from '../helpers/test-utils';

describe('My Feature Tests', () => {
  let testContext: ReturnType<typeof createTestContext>;

  beforeEach(() => {
    if (skipIfConfigInvalid()) return;
    testContext = createTestContext({ test: 'my-feature' });
  });

  it('should test real API functionality', async () => {
    if (skipIfConfigInvalid()) {
      console.log('⏭️  Skipping: No valid test configuration');
      return;
    }

    const { sdk } = testContext;
    // Your test code here - runs against real API
  });
});

Custom Test Utilities

import { TestDataGenerator, retry, wait } from '../helpers/test-utils';

// Generate test data
const testUser = TestDataGenerator.testObject({
  email: TestDataGenerator.randomEmail()
});

// Retry with backoff
const result = await retry(() => sdk.get('/endpoint'), 3);

// Rate limiting
await wait(1000);

Performance Testing

import { testUtils } from '../helpers/test-utils';

testUtils.performance.start('operation');
await sdk.post('/endpoint', data);
const duration = testUtils.performance.end('operation');

expect(duration).toBeLessThan(5000);

Test Environment Settings

The framework automatically adapts to different environments:

  • Development: Lenient timeouts, detailed logging
  • Staging: Production-like settings, real data validation
  • Production: Conservative timeouts, minimal test impact

📊 Test Reports

Tests generate comprehensive reports including:

  • Pass/Fail Status - Detailed test results
  • ⏱️ Performance Metrics - Request timing and throughput
  • 🔍 Error Analysis - Real API error response validation
  • 📈 Coverage Reports - Code coverage analysis
  • 🌍 Environment Info - Test configuration and context

🔗 API Compatibility

Based on the BCB API OpenAPI specification:

Authentication Endpoint

  • POST /auth/oauth/token - OAuth2 client credentials flow

Accounts Endpoints

  • GET /v3/accounts - List accounts with optional filtering and pagination
    • Query Parameters:
      • counterparty_id - Filter by counterparty ID
      • limit - Limit result set for server-side paging
      • pageToken - Offset result set when paging
      • id - Return single result with matching account ID

Balances Endpoints

  • GET /v3/balances/{account_id} - Get balance for a specific account
    • Path Parameters:
      • account_id - Account ID (numeric or string)
    • Query Parameters:
      • dateTo - Get end-of-day balance for specific date (YYYY-MM-DD format)

Transactions Endpoints

  • GET /v3/accounts/{account_id}/transactions - List transactions for a specific account

    • Path Parameters:
      • account_id - Account ID (numeric only)
    • Query Parameters:
      • dateFrom - Start date for filtering (YYYY-MM-DD format)
      • dateTo - End date for filtering (YYYY-MM-DD format)
      • limit - Limit result set for server-side paging
      • pageToken - Offset result set when paging
  • GET /v3/accounts/{account_id}/transactions/{tx_id} - Get transaction detail by ID

    • Path Parameters:
      • account_id - Account ID (numeric or string)
      • tx_id - Transaction ID (unique code)

Beneficiaries Endpoints

  • GET /v3/beneficiaries - List beneficiaries with optional filtering and pagination
    • Query Parameters:
      • limit - Limit result set for server-side paging
      • pageToken - Offset result set when paging

BLINC Network Endpoints

  • GET /v3/accounts/{account_id}/blinc-beneficiaries - List BLINC beneficiaries for an account

    • Path Parameters:
      • account_id - BLINC account ID of counterparty requesting connected BLINC members list
    • Query Parameters:
      • ccy - Restrict response to BLINC accounts with matching currency (ISO 4217 format)
  • GET /v3/blinc-accounts/{blinc_id} - Get BLINC member by BLINC ID

    • Path Parameters:
      • blinc_id - BLINC ID of another BLINC member (numeric)

Payments Endpoints (V5 API)

  • POST /v5/payments/authorise - Authorize payment with enhanced address fields (V5)
    • Request Body: PaymentRequest object with:
      • Required: counterparty_id, sender_account_id, ccy, amount, reference, reason, preferred_scheme
      • V5 Mandatory Address: beneficiary_address_line_1, beneficiary_city, beneficiary_country
      • Beneficiary: Either beneficiary_account_id OR beneficiary details (beneficiary_name, banking details)
      • Currency Specific: GBP requires sort code, USD requires routing number, EUR requires BIC
      • CHAPS Specific: beneficiary_postcode and beneficiary_lei_code mandatory for high-value GBP payments
      • Schemes: AUTO (external), BLINC (network), INTERNAL (same counterparty)

Beneficiary Creation Endpoints (V4 API)

  • POST /v4/accounts - Create beneficiary account with enhanced address fields
    • Request Body: CreateBeneficiaryRequest object with:
      • Required: counterparty_id, cid, name, account_type, ccy, node_name, node_type, bcb_controlled: 1, is_beneficiary: 1
      • Account Type Specific: Bank accounts require IBAN or account number, wallets require wallet address
      • Address Fields: Enhanced V4 address structure with separate fields for line 1-3, city, region, postcode
      • Currency Specific: USD in US requires routing number, GBP in UK requires sort code and full address for CHAPS

VOP (Verification of Payee) Endpoints (Client API)

  • POST /v1/accounts/{accountId}/payments/{e2eId}/verification-of-payee/approve - Approve a payment where the beneficiary name does not match the name on the account

    • Path Parameters:
      • accountId - The account ID (numeric)
      • e2eId - The end-to-end ID of the payment (string)
    • Response: 202 (Accepted), 401 (Unauthorized), 404 (No unverified payment found), 409 (Payment is not unverified)
    • Note: Uses Client API endpoint (client-api.bcb.group)
  • POST /v1/accounts/{accountId}/payments/{e2eId}/verification-of-payee/cancel - Cancel a payment where the beneficiary name does not match the name on the account

    • Path Parameters:
      • accountId - The account ID (numeric)
      • e2eId - The end-to-end ID of the payment (string)
    • Response: 202 (Accepted), 401 (Unauthorized), 404 (No unverified payment found), 409 (Payment is not unverified)
    • Note: Uses Client API endpoint (client-api.bcb.group)

Virtual Accounts Endpoints (Client API)

  • POST /v2/accounts/{accountId}/virtual - Create virtual accounts for given owners (V2 Client API endpoint)

    • Path Parameters:
      • accountId - The account id for the segregated account (numeric)
    • Request Body: Array of VirtualAccountOwner objects with:
      • Required: correlationId (UUID format), name, addressLine1, city, postcode, country, nationality, isIndividual
      • Individual Specific: dateOfBirth (YYYY-MM-DD format) required for isIndividual: true
      • Company Specific: registrationNumber required for isIndividual: false
      • Optional Bank Details: iban + bicSwift for EUR, accountNumber + sortCode for GBP (owner's external bank details)
      • Address Fields: addressLine2, addressLine3, region optional
      • Limits: Maximum 1000 owners per request
  • GET /v1/accounts/{accountId}/virtual/all-account-data - List virtual accounts (V1 Client API endpoint)

    • Path Parameters:
      • accountId - The account id for the segregated account (numeric)
    • Query Parameters:
      • pageIndex - The page offset (optional, default: 0)
      • pageSize - The limit of results (optional, default: 1000)
    • Response: Virtual accounts with their generated IBANs and status
  • POST /v1/accounts/{accountId}/virtual/{iban}/close - Close virtual account by IBAN (V1 Client API endpoint)

    • Path Parameters:
      • accountId - The account id for the segregated account (numeric)
      • iban - The IBAN of the virtual account to close (generated virtual IBAN, not owner's external IBAN)
    • Response: 202 (Accepted) or 404 (Account/IBAN not found)

Response Format

All API responses follow the standard BCB format with:

  • data - Response payload
  • status - HTTP status code
  • headers - Response headers
  • requestId - Unique request identifier

🛡️ Security

  • ✅ Credentials stored securely in environment variables
  • ✅ No hardcoded secrets in code
  • ✅ Token automatic refresh and caching
  • ✅ Proper error handling without data leakage

📚 Additional Resources

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: npm test
  5. Submit a pull request

📄 License

MIT License - see LICENSE file for details.


Built with ❤️ for developers who need reliable, well-tested SDKs that work against real APIs.