{"_id":"@arminbcbtest/sdk","_rev":"2-257e4a7fdeae9e7e891bf1ebd9b7d272","name":"@arminbcbtest/sdk","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@arminbcbtest/sdk","version":"0.1.0","keywords":["bcb","api","sdk","typescript","javascript","testing","acceptance-testing"],"author":{"name":"BCB Group"},"license":"MIT","_id":"@arminbcbtest/sdk@0.1.0","maintainers":[{"name":"arminbcb","email":"armin@bcbgroup.io"}],"homepage":"https://gitlab.com/bcb-projects/bcb-connect","bugs":{"url":"https://gitlab.com/bcb-projects/bcb-connect"},"dist":{"shasum":"c03375b1add8e75be6ff347069327bc2f0588374","tarball":"https://registry.npmjs.org/@arminbcbtest/sdk/-/sdk-0.1.0.tgz","fileCount":94,"integrity":"sha512-ZssA3u0/qfujSoybuuWgOOGYNCk/sR+yk45dOxYjTlXqQJIJ3CfZi+gIY1dOQRx0QNPmmG6Ocf9Hp3a7MDNYIQ==","signatures":[{"sig":"MEYCIQCQHvJthIoRhb72C98F3JABOkuTJghbOyWCo3UhUsDO/wIhAOa7owihLuR8+OPWJQulQRDTLgceWL44MOgo5qk1Mkun","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":765289},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/esm/index.js","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/esm/index.js","require":"./dist/index.js"}},"gitHead":"fb08b0f09fb864d18caf1c0d3eb3166006e712cc","scripts":{"dev":"ts-node scripts/dev.ts","lint":"eslint src","test":"jest","build":"npm run build:cjs && npm run build:esm","clean":"rm -rf dist","start":"nest start","format":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","lint:fix":"eslint --fix src","build:cjs":"tsc -p tsconfig.json","build:esm":"tsc -p tsconfig.esm.json","start:dev":"nest start --watch","test:unit":"jest --testPathPatterns=unit","typecheck":"tsc --noEmit","start:prod":"node dist/server/main.js","test:debug":"node --inspect-brk node_modules/.bin/jest --runInBand","test:watch":"jest --watch","publish:npm":"npm run clean && npm run build && npm publish","build:server":"tsc -p tsconfig.server.json","test:coverage":"jest --coverage","version:major":"npm version major","version:minor":"npm version minor","version:patch":"npm version patch","prepublishOnly":"npm run clean && npm run build","test:env:check":"ts-node scripts/check-test-env.ts","typecheck:test":"tsc --noEmit -p tsconfig.test.json","test:acceptance":"jest --testPathPatterns=acceptance","test:integration":"jest --testPathPatterns=integration","publish-prerelease":"if [[ $(node npm-scripts/is-prerelease-version.js) == 'true' ]]; then npm run build --silent && npm publish; else echo \"Cannot publish $npm_package_version until it's merged to main. If you want to publish a prerelease, please run 'npm run prerelease --preid your-feature-name' and push your updated branch\"; fi","version:prerelease":"npm version prerelease --preid=beta"},"_npmUser":{"name":"arminbcb","email":"armin@bcbgroup.io"},"repository":{"url":"git+ssh://git@gitlab.com/bcb-projects/bcb-connect.git","type":"git"},"_npmVersion":"11.7.0","description":"The official BCB TypeScript SDK with comprehensive acceptance testing","directories":{},"sideEffects":false,"_nodeVersion":"20.19.2","dependencies":{"axios":"^1.13.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0","rxjs":"^7.8.1","uuid":"11.0.0","dotenv":"17.2.3","eslint":"9.38.0","ts-node":"10.9.2","prettier":"3.6.2","@swc/core":"^1.10.0","@swc/jest":"^0.2.38","typescript":"5.9.3","@nestjs/cli":"^10.4.5","@types/jest":"30.0.0","@types/node":"20.9.5","@types/uuid":"^10.0.0","@nestjs/core":"^10.4.2","jest-extended":"6.0.0","@nestjs/common":"^10.4.2","@types/express":"^4.17.21","@nestjs/testing":"^10.4.2","reflect-metadata":"^0.2.2","@nestjs/schematics":"^10.1.4","eslint-plugin-jest":"^29.0.1","eslint-plugin-prettier":"^5.5.4","@nestjs/platform-express":"^10.4.2","@typescript-eslint/parser":"8.46.2","@typescript-eslint/eslint-plugin":"8.46.2"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0_1776959118719_0.5165327884921023","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@arminbcbtest/sdk","version":"0.1.1","description":"The official BCB TypeScript SDK with comprehensive acceptance testing","main":"dist/index.js","module":"dist/esm/index.js","types":"dist/index.d.ts","sideEffects":false,"exports":{".":{"import":"./dist/esm/index.js","require":"./dist/index.js","types":"./dist/index.d.ts"}},"repository":{"type":"git","url":"git+ssh://git@gitlab.com/bcb-projects/bcb-connect.git"},"publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"scripts":{"build":"npm run build:cjs && npm run build:esm","build:cjs":"tsc -p tsconfig.json","build:esm":"tsc -p tsconfig.esm.json","build:server":"tsc -p tsconfig.server.json","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build","start":"nest start","start:dev":"nest start --watch","start:prod":"node dist/server/main.js","test":"jest","test:unit":"jest --testPathPatterns=unit","test:integration":"jest --testPathPatterns=integration","test:acceptance":"jest --testPathPatterns=acceptance","test:watch":"jest --watch","test:coverage":"jest --coverage","test:debug":"node --inspect-brk node_modules/.bin/jest --runInBand","lint":"eslint src","lint:fix":"eslint --fix src","format":"prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"","dev":"ts-node scripts/dev.ts","typecheck":"tsc --noEmit","typecheck:test":"tsc --noEmit -p tsconfig.test.json","test:env:check":"ts-node scripts/check-test-env.ts","publish-prerelease":"if [[ $(node npm-scripts/is-prerelease-version.js) == 'true' ]]; then npm run build --silent && npm publish; else echo \"Cannot publish $npm_package_version until it's merged to main. If you want to publish a prerelease, please run 'npm run prerelease --preid your-feature-name' and push your updated branch\"; fi","version:patch":"npm version patch","version:minor":"npm version minor","version:major":"npm version major","version:prerelease":"npm version prerelease --preid=beta","publish:npm":"npm run clean && npm run build && npm publish"},"keywords":["bcb","api","sdk","typescript","javascript","testing","acceptance-testing"],"author":{"name":"BCB Group"},"license":"MIT","engines":{"node":">=20"},"devDependencies":{"@nestjs/cli":"^10.4.5","@nestjs/common":"^10.4.2","@nestjs/core":"^10.4.2","@nestjs/platform-express":"^10.4.2","@nestjs/schematics":"^10.1.4","@nestjs/testing":"^10.4.2","@swc/core":"^1.10.0","@swc/jest":"^0.2.38","@types/express":"^4.17.21","@types/jest":"30.0.0","@types/node":"20.9.5","@types/uuid":"^10.0.0","@typescript-eslint/eslint-plugin":"8.46.2","@typescript-eslint/parser":"8.46.2","dotenv":"17.2.3","eslint":"9.38.0","eslint-plugin-jest":"^29.0.1","eslint-plugin-prettier":"^5.5.4","jest":"30.2.0","jest-extended":"6.0.0","prettier":"3.6.2","reflect-metadata":"^0.2.2","rxjs":"^7.8.1","ts-node":"10.9.2","typescript":"5.9.3","uuid":"11.0.0"},"dependencies":{"axios":"^1.13.2"},"bugs":{"url":"https://gitlab.com/bcb-projects/bcb-connect"},"homepage":"https://gitlab.com/bcb-projects/bcb-connect","gitHead":"3d972ad5757ccec326cfb100c0d0ddc242f159cc","_id":"@arminbcbtest/sdk@0.1.1","_nodeVersion":"20.19.2","_npmVersion":"11.7.0","dist":{"integrity":"sha512-tnPZoNYKF+9lQizT12IinUrfQAJHDeFjP4P7zsGd9MmBijxNyKCotvaTuKtkiHT3GwYrxPMQ4mwRlGFAaTn16g==","shasum":"00d4ed71c1158573bf2c9069cdd87d23dfb94b85","tarball":"https://registry.npmjs.org/@arminbcbtest/sdk/-/sdk-0.1.1.tgz","fileCount":94,"unpackedSize":765289,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCWTLRNs8ZYXAs9uyvRS5Gr73gDxV/u2seJOMOyw9cy9AIgJd+WLIUPW5j5TQUVC/l/oClvK8qtBMkJ9NnSapI+AJw="}]},"_npmUser":{"name":"arminbcb","email":"armin@bcbgroup.io"},"directories":{},"maintainers":[{"name":"arminbcb","email":"armin@bcbgroup.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.1.1_1776959632288_0.7127572718847102"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-23T15:45:18.622Z","modified":"2026-04-23T15:53:52.666Z","0.1.0":"2026-04-23T15:45:18.918Z","0.1.1":"2026-04-23T15:53:52.507Z"},"bugs":{"url":"https://gitlab.com/bcb-projects/bcb-connect"},"author":{"name":"BCB Group"},"license":"MIT","homepage":"https://gitlab.com/bcb-projects/bcb-connect","keywords":["bcb","api","sdk","typescript","javascript","testing","acceptance-testing"],"repository":{"type":"git","url":"git+ssh://git@gitlab.com/bcb-projects/bcb-connect.git"},"description":"The official BCB TypeScript SDK with comprehensive acceptance testing","maintainers":[{"name":"arminbcb","email":"armin@bcbgroup.io"}],"readme":"# BCB Connect - TypeScript SDK\n\nThe 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.\n\n## 🚀 Quick Start\n\n### Installation\n\n```bash\nnpm install @bcb/sdk\n```\n\n### Basic Usage\n\n```typescript\nimport BCB from '@bcb/sdk';\n\nconst bcb = new BCB({\n  clientId: 'your_client_id',\n  clientSecret: 'your_client_secret',\n  environment: 'sandbox', // 'sandbox' or 'production'\n});\n\n// The SDK handles authentication automatically\n// List all accounts\nconst accounts = await bcb.accounts.list();\nconsole.log(`Found ${accounts.data.length} accounts`);\n\n// Get a specific account\nif (accounts.data.length > 0) {\n  const account = await bcb.accounts.retrieve(accounts.data[0].id);\n  console.log(`Account: ${account.data.account_label}`);\n  \n  // Get account balance\n  const balance = await bcb.balances.get(account.data.id);\n  console.log(`Balance: ${balance.data.length} entries`);\n  \n  // Get recent transactions\n  const transactions = await bcb.transactions.list(account.data.id, { limit: 10 });\n  console.log(`Recent transactions: ${transactions.data.length}`);\n  \n  // Make a payment\n  const payment = await bcb.payments.createGBPFPS({\n    counterparty_id: account.data.counterparty_id,\n    sender_account_id: account.data.id,\n    beneficiary_name: 'John Smith',\n    beneficiary_account_number: '12345678',\n    beneficiary_sort_code: '200000',\n    amount: '100.50',\n    reference: 'Invoice payment',\n    reason: 'GDSV' // Purchase/Sale of Goods and Services\n  });\n  console.log(`Payment created: ${payment.data[0].transactionId}`);\n}\n```\n\n## 🧪 Comprehensive Testing Infrastructure\n\nThis 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:\n\n### Test Architecture\n\n- **Unit Tests** (`tests/unit/`) - Fast, isolated component testing\n- **Integration Tests** (`tests/integration/`) - Real API integration testing  \n- **Acceptance Tests** (`tests/acceptance/`) - End-to-end scenarios and performance testing\n\n### Key Testing Features\n\n✅ **Real API Testing** - Tests run against actual BCB servers, not mocks  \n✅ **Multi-Environment Support** - Development, staging, production configurations  \n✅ **Performance Monitoring** - Built-in performance tracking and assertions  \n✅ **Custom Jest Matchers** - BCB-specific test assertions  \n✅ **Comprehensive Error Testing** - Real error scenario validation  \n✅ **Load Testing** - Concurrent request and performance testing  \n✅ **Event System Testing** - SDK event emission validation  \n\n### Running Tests\n\n```bash\n# Check test environment configuration\nnpm run test:env:check\n\n# Run all tests\nnpm test\n\n# Run specific test types\nnpm run test:unit         # Unit tests only\nnpm run test:integration  # Integration tests with real API\nnpm run test:acceptance   # Full acceptance test suite\n\n# Development testing\nnpm run test:watch        # Watch mode\nnpm run test:coverage     # Coverage report\n```\n\n### Test Configuration\n\n1. **Copy environment template:**\n   ```bash\n   cp env.example .env\n   ```\n\n2. **Configure for your environment:**\n   ```bash\n   # Required: BCB API credentials\n   BCB_CLIENT_ID=your_client_id\n   BCB_CLIENT_SECRET=your_client_secret\n   \n   # Environment configuration (sandbox or production)\n   BCB_ENVIRONMENT=sandbox  # sandbox|production\n   \n   # Optional: Override specific URLs (if not using predefined environments)\n   # BCB_BASE_URL=https://api.bcb.group\n   # BCB_AUTH_URL=https://auth.bcb.group\n   # BCB_CLIENT_API_URL=https://client-api.bcb.group\n   \n   # Optional configuration\n   BCB_TIMEOUT=30000           # Request timeout in ms\n   BCB_MAX_RETRIES=3           # Max retry attempts\n   ```\n\n3. **Verify configuration:**\n   ```bash\n   npm run test:env:check\n   ```\n\n## 📖 SDK Documentation\n\n### Environment Configuration\n\nThe BCB SDK supports multiple environments with predefined URL configurations:\n\n#### Predefined Environments\n\n```typescript\nimport BCB from '@bcb/sdk';\n\n// Sandbox environment (recommended for development and testing)\nconst sandboxBcb = new BCB({\n  clientId: 'your_client_id',\n  clientSecret: 'your_client_secret',\n  environment: 'sandbox', // Uses *.uat.bcb.group URLs\n});\n\n// Production environment\nconst prodBcb = new BCB({\n  clientId: 'your_client_id',\n  clientSecret: 'your_client_secret',\n  environment: 'production', // Uses *.bcb.group URLs (default)\n});\n```\n\n#### Environment URLs\n\n| Environment | Auth URL | Base API URL | Client API URL |\n|-------------|----------|--------------|----------------|\n| **sandbox** | `https://auth.uat.bcb.group` | `https://api.uat.bcb.group` | `https://client-api.uat.bcb.group` |\n| **production** | `https://auth.bcb.group` | `https://api.bcb.group` | `https://client-api.bcb.group` |\n\n#### Custom URLs\n\nYou can also override specific URLs when needed:\n\n```typescript\nconst bcb = new BCB({\n  clientId: 'your_client_id',\n  clientSecret: 'your_client_secret',\n  // Custom URLs override environment settings\n  baseURL: 'https://custom-api.example.com',\n  authURL: 'https://custom-auth.example.com',\n  clientApiURL: 'https://custom-client-api.example.com',\n});\n```\n\n#### Multiple API Endpoints\n\nThe SDK provides methods to interact with different API endpoints:\n\n```typescript\n// Base API requests (most common)\nconst response = await bcb.request('/endpoint');\n\n// Auth API requests (handled automatically, but available if needed)\nconst authResponse = await bcb.authRequest('/oauth/token');\n\n// Client API requests\nconst clientResponse = await bcb.clientRequest('/client-endpoint');\n```\n\n### Configuration\n\n```typescript\nimport { BCB } from '@bcb/sdk';\n\nconst bcb = new BCB({\n  clientId: 'your_client_id',\n  clientSecret: 'your_client_secret',\n  baseURL: 'https://api.bcb.group', // optional\n  timeout: 30000, // optional\n  maxRetries: 3, // optional\n});\n```\n\n### Authentication\n\nThe SDK handles OAuth2 client credentials flow automatically:\n\n```typescript\n// Authentication happens automatically on first API call\nconst response = await bcb.get('/protected-endpoint');\n\n// Manual token management\nconst token = await bcb.auth.getAccessToken();\nconst isAuth = bcb.auth.isAuthenticated();\nawait bcb.auth.refreshToken();\n```\n\n### Accounts Management\n\nThe SDK provides comprehensive account listing functionality with Stripe-like patterns:\n\n```typescript\n// List all accounts\nconst accounts = await bcb.accounts.list();\nconsole.log(`Found ${accounts.data.length} accounts`);\n\n// List accounts with pagination\nconst paginatedAccounts = await bcb.accounts.list({\n  limit: 10,\n  pageToken: 123\n});\n\n// Filter by counterparty\nconst counterpartyAccounts = await bcb.accounts.listByCounterparty(483);\n\n// Retrieve specific account by ID\nconst account = await bcb.accounts.retrieve(378);\nconsole.log(`Account: ${account.data.account_label}`);\n\n// Filter by currency (client-side filtering)\nconst btcAccounts = await bcb.accounts.listByCurrency('BTC');\n\n// Filter by account type (client-side filtering)\nconst wallets = await bcb.accounts.listByType('Wallet');\nconst banks = await bcb.accounts.listByType('Bank');\nconst custodial = await bcb.accounts.listByType('Custodial');\n\n// Advanced filtering with multiple parameters\nconst filteredAccounts = await bcb.accounts.listByCounterparty(483, {\n  limit: 5\n});\n```\n\n### Balance Management\n\nThe SDK provides comprehensive balance functionality following the same Stripe-like patterns:\n\n```typescript\n// Get current balance for an account (using numeric account ID)\nconst balance = await bcb.balances.get(539);\nconsole.log(`Found ${balance.data.length} balance entries`);\n\n// Get balance using account ID from accounts list\nconst accounts = await bcb.accounts.list();\nconst accountId = accounts.data[0].id; // Use the numeric ID\nconst balance = await bcb.balances.get(accountId);\n\n// Get end-of-day balance for a specific date\nconst eodBalance = await bcb.balances.getEndOfDay(539, '2023-12-31');\n\n// Using dateTo parameter directly\nconst dateBalance = await bcb.balances.get(539, { dateTo: '2023-12-31' });\n\n// Retrieve method (alias for get)\nconst balance = await bcb.balances.retrieve(539);\n\n// Get balances for multiple accounts\nconst balances = await bcb.balances.getMultiple([539, 540]);\n\n// Filter balance by currency (client-side filtering)\nconst btcBalance = await bcb.balances.getByCurrency(539, 'BTC');\n\n// Filter balance by account type (client-side filtering)\nconst walletBalance = await bcb.balances.getByType(539, 'Wallet');\nconst bankBalance = await bcb.balances.getByType(539, 'Bank');\nconst custodialBalance = await bcb.balances.getByType(539, 'Custodial');\n```\n\n#### Balance Object Structure\n\n```typescript\ninterface Balance {\n  counterparty_id: number;\n  counterparty_name: string;\n  counterparty_country: string;\n  account_id: number;\n  cid: string;              // Alpha-numeric counterparty id\n  pid: string;              // Alpha-numeric white-label partner id\n  partner_id: number;       // Internal numeric white-label partner id\n  account_label: string;\n  partner_name: string;     // White-label partner name\n  account_name: string;     // Account holder name\n  node_address: string;\n  node_location: string | null;\n  ticker: string;           // Asset identifier (e.g. 'BTC', 'USD')\n  balance: number;          // Latest balance\n  email: string;            // Account holder email address\n  aid: string;              // Alpha-numeric account id\n  account_type: 'Bank' | 'Custodial' | 'Wallet';  // Account type\n  payment_type: string | null;\n  description: string;      // Free-text description of account\n  bcb_controlled: number;   // Flag is 1 if BCB can operate the account, 0 otherwise\n  is_observable: number;\n  host_name: string;\n  reporting_ccy: string;    // Home currency of client\n  iban: string;\n  bic: string;\n  host_location: string;\n  host_hub: string;\n  host_country: string;\n  host_country_name: string;\n  asset_name: string;       // Common name for the asset identified by the ticker\n  display_name: string;     // More descriptive name of the asset\n  image_url_trans: string;  // Image URL of asset for display purposes\n  decimals_price: number;   // Number of decimals to display in the price\n  decimals_quantity: number; // Number of decimals to display in the quantity\n}\n```\n\n#### Account Object Structure\n\n```typescript\ninterface Account {\n  id: number;\n  counterparty_id: number;\n  aid: string;              // Account identifier\n  cid: string;              // Customer identifier\n  account_type: 'Wallet' | 'Bank' | 'Custodial';\n  ccy: string;              // Currency code\n  host_name: string;        // Host institution name\n  host_hub: string;         // Host hub location\n  host_location: string;    // Host physical location\n  node_name: string;        // Node name\n  node_address: string;     // Node address\n  node_location: string;    // Node location\n  node_country: string;     // Node country code\n  node_subaddress: string;  // Node subaddress\n  iban: string;             // IBAN (for bank accounts)\n  account_label: string;    // Human-readable label\n  description: string;      // Account description\n  bcb_controlled: number;   // BCB controlled flag (0/1)\n  settlement_reference: string;\n  created_at: string;       // ISO timestamp\n  updated_at: string;       // ISO timestamp\n  partner_id: number;\n  email: string;            // Contact email\n  reporting_ccy: string;    // Reporting currency\n  pid: string;              // Partner identifier\n  partner_name: string;     // Partner name\n}\n```\n\n### Transaction Management\n\nThe SDK provides comprehensive transaction functionality for retrieving transaction history with advanced filtering options:\n\n```typescript\n// List all transactions for an account (using numeric account ID)\nconst transactions = await bcb.transactions.list(539);\nconsole.log(`Found ${transactions.data.length} transactions`);\n\n// List transactions using account ID from accounts list\nconst accounts = await bcb.accounts.list();\nconst accountId = accounts.data[0].id; // Use the numeric ID\nconst transactions = await bcb.transactions.list(accountId);\n\n// List transactions with pagination\nconst paginatedTransactions = await bcb.transactions.list(539, {\n  limit: 10,\n  pageToken: 123\n});\n\n// Filter by date range\nconst dateRangeTransactions = await bcb.transactions.listByDateRange(\n  539, \n  '2023-01-01', \n  '2023-12-31'\n);\n\n// Using date parameters directly\nconst filteredTransactions = await bcb.transactions.list(539, {\n  dateFrom: '2023-01-01',\n  dateTo: '2023-12-31',\n  limit: 50\n});\n\n// Filter by transaction type (client-side filtering)\nconst creditTransactions = await bcb.transactions.listCredits(539);\nconst debitTransactions = await bcb.transactions.listDebits(539);\n\n// Get transactions for multiple accounts\nconst multipleTransactions = await bcb.transactions.listMultiple([539, 540]);\n\n// Alias method for consistency\nconst transactions = await bcb.transactions.listForAccount(539);\n\n// Advanced filtering with multiple parameters\nconst advancedTransactions = await bcb.transactions.list(539, {\n  dateFrom: '2023-01-01',\n  dateTo: '2023-12-31',\n  limit: 20\n});\n\n// Get specific transaction detail by ID\nconst transactionDetail = await bcb.transactions.retrieveDetail(539, '7b73263d-65dc-11e9-b39c-42010a8400a9');\nconsole.log(`Transaction: ${transactionDetail.data.tx_id}`);\nconsole.log(`Amount: ${transactionDetail.data.amount} ${transactionDetail.data.ticker}`);\nconsole.log(`Network: ${transactionDetail.data.network}`);\nconsole.log(`Type: ${transactionDetail.data.credit ? 'Credit' : 'Debit'}`);\nconsole.log(`Status: ${transactionDetail.data.approved ? 'Approved' : 'Pending'}`);\n\n// Access detailed bank information\nif (transactionDetail.data.details.iban) {\n  console.log(`IBAN: ${transactionDetail.data.details.iban}`);\n  console.log(`Account Name: ${transactionDetail.data.details.account_name}`);\n  console.log(`Reference: ${transactionDetail.data.details.reference}`);\n}\n\n// Retrieve alias method (same as retrieveDetail)\nconst transaction = await bcb.transactions.retrieve(539, '7b73263d-65dc-11e9-b39c-42010a8400a9');\n```\n\n#### Transaction Object Structure\n\n```typescript\ninterface Transaction {\n  tx_id: string;                    // Unique transaction ID\n  account_id: number;               // Internal numeric account ID\n  blinc_id?: string | null;         // BLINC network ID (if applicable)\n  network?: string;                 // Payment network (e.g., 'FPS', 'Bitcoin')\n  value_date: string;               // Transaction date (ISO timestamp)\n  credit: number;                   // 1 for credit, 0 for debit\n  details: TransactionDetails;      // Transaction details object\n  ticker: string;                   // Asset identifier (e.g., 'BTC', 'GBP', 'USD')\n  amount: number;                   // Transaction amount\n  approved: number;                 // 1 for approved, 0 for pending\n  notes?: string | null;            // Optional transaction notes\n  source_name?: string;             // Source system name (e.g., 'clearbank')\n}\n\ninterface TransactionDetails {\n  bic?: string | null;              // Bank Identifier Code\n  iban?: string | null;             // International Bank Account Number\n  blinc_id?: string | null;         // BLINC network identifier\n  reference?: string;               // Payment reference\n  sort_code?: string;               // Bank sort code\n  account_name?: string;            // Account holder name\n  account_number?: string;          // Account number\n  counterparty_reference?: string;  // Counterparty reference\n  endToEndIdentifier?: string;      // End-to-end identifier\n  nonce?: number;                   // Unique nonce value\n}\n```\n\n### HTTP Methods\n\n```typescript\n// GET request\nconst users = await bcb.get('/users');\n\n// POST request\nconst newUser = await bcb.post('/users', {\n  name: 'John Doe',\n  email: 'john@example.com',\n});\n\n// PUT, PATCH, DELETE\nconst updated = await bcb.put('/users/123', userData);\nconst patched = await bcb.patch('/users/123', { name: 'Jane' });\nawait bcb.delete('/users/123');\n```\n\n### Error Handling\n\n```typescript\nimport { \n  BCBAuthenticationError,\n  BCBRateLimitError,\n  BCBAPIError \n} from '@bcb/sdk';\n\ntry {\n  const response = await bcb.get('/endpoint');\n} catch (error) {\n  if (error instanceof BCBAuthenticationError) {\n    console.error('Auth failed:', error.message);\n  } else if (error instanceof BCBRateLimitError) {\n    console.error('Rate limited:', error.headers?.['retry-after']);\n  }\n}\n```\n\n### Beneficiaries Management\n\nThe SDK provides comprehensive beneficiary management functionality following the same Stripe-like patterns:\n\n```typescript\n// List all beneficiaries\nconst beneficiaries = await bcb.beneficiaries.list();\nconsole.log(`Found ${beneficiaries.data.length} beneficiaries`);\n\n// List beneficiaries with pagination\nconst paginatedBeneficiaries = await bcb.beneficiaries.list({\n  limit: 10,\n  pageToken: 123\n});\n\n// Retrieve specific beneficiary by ID\nconst beneficiary = await bcb.beneficiaries.retrieve(378);\nconsole.log(`Beneficiary: ${beneficiary.data.account_label}`);\n\n// Filter by currency (client-side filtering)\nconst btcBeneficiaries = await bcb.beneficiaries.listByCurrency('BTC');\n\n// Filter by account type (client-side filtering)\nconst wallets = await bcb.beneficiaries.listByType('Wallet');\nconst banks = await bcb.beneficiaries.listByType('Bank');\nconst custodial = await bcb.beneficiaries.listByType('Custodial');\n\n// Filter by counterparty (client-side filtering)\nconst counterpartyBeneficiaries = await bcb.beneficiaries.listByCounterparty(483);\n\n// Advanced filtering with multiple parameters\nconst filteredBeneficiaries = await bcb.beneficiaries.listByCurrency('BTC', {\n  limit: 5\n});\n```\n\n#### Beneficiary Object Structure\n\n```typescript\ninterface Beneficiary {\n  id: number;                       // Numeric internal beneficiary ID\n  counterparty_id: number;          // Numeric internal counterparty ID\n  aid: string;                      // Alpha-numeric beneficiary ID\n  cid: string;                      // Alpha-numeric counterparty ID\n  account_type: 'Wallet' | 'Bank' | 'Custodial';  // Account type\n  ccy: string;                      // Asset ticker, or left hand side of trading symbol\n  host_name: string;                // Bank Name for bank accounts, Custodian Name for custody accounts, Wallet Name for wallet accounts\n  host_hub: string;                 // Branch Code for bank accounts, optional for custody and wallets\n  host_location: string;            // Bank Address for bank accounts, optional for custody and wallets\n  node_name: string;                // Account holder's legal name\n  node_address: string;             // Account Number for bank accounts, wallet address for wallets, optional for custody\n  node_location: string;            // Account holder's residential address for bank accounts, optional memo field for custody and wallets\n  node_country: string;             // Account holder's residential address country code (2-letter ISO country code), optional memo field for custody and wallets\n  node_subaddress?: string;         // Optional sub-address for wallets\n  iban: string;                     // IBAN for bank accounts\n  account_label: string;            // Display name for beneficiary\n  description: string;              // Free-text description of beneficiary\n  settlement_reference: string;     // Optional settlement reference for payments\n  created_at: string;               // Timestamp of account record creation (YYYY-MM-DDTHH:MM:SS.000Z)\n  updated_at: string;               // Timestamp of account record update (YYYY-MM-DDTHH:MM:SS.000Z)\n  partner_id: number;               // Internal numeric ID of white-label partner\n  email: string;                    // Email address of beneficiary\n  pid: string;                      // Alpha-numeric ID of white-label partner\n  partner_name: string;             // White-label partner name\n}\n```\n\n#### Beneficiary Usage Examples\n\n```typescript\n// Display beneficiary details\nconst beneficiary = await bcb.beneficiaries.retrieve(378);\nconsole.log('Beneficiary Details:');\nconsole.log(`  Label: ${beneficiary.data.account_label}`);\nconsole.log(`  Type: ${beneficiary.data.account_type}`);\nconsole.log(`  Currency: ${beneficiary.data.ccy}`);\nconsole.log(`  Host: ${beneficiary.data.host_name}`);\nconsole.log(`  Partner: ${beneficiary.data.partner_name}`);\n\nif (beneficiary.data.iban) {\n  console.log(`  IBAN: ${beneficiary.data.iban}`);\n}\n\nif (beneficiary.data.node_address) {\n  console.log(`  Address: ${beneficiary.data.node_address}`);\n}\n\n// Filter and analyze beneficiaries\nconst allBeneficiaries = await bcb.beneficiaries.list();\n\n// Group by currency\nconst byCurrency = allBeneficiaries.data.reduce((acc, b) => {\n  acc[b.ccy] = (acc[b.ccy] || 0) + 1;\n  return acc;\n}, {} as Record<string, number>);\n\nconsole.log('Beneficiaries by currency:', byCurrency);\n\n// Group by account type\nconst byType = allBeneficiaries.data.reduce((acc, b) => {\n  acc[b.account_type] = (acc[b.account_type] || 0) + 1;\n  return acc;\n}, {} as Record<string, number>);\n\nconsole.log('Beneficiaries by type:', byType);\n```\n\n### Payments Management (V5 API)\n\nThe SDK provides comprehensive payment functionality using the latest V5 payments endpoint with enhanced address validation and support for multiple payment schemes and currencies.\n\n#### Purpose Code Validation\n\nAll 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:\n\n```typescript\n// ✅ Valid reason codes (examples)\n'BKFE' // Bank loan fees\n'GDSV' // Purchase/Sale of Goods and Services  \n'SALA' // Salary Payment\n'RENT' // Rent payment\n'TAXS' // Tax Payment\n'SERV' // Service charges\n// ... and 100+ other valid codes\n\n// ❌ Invalid reason codes will throw an error\nreason: 'invalid-code' // Error: Invalid reason code 'invalid-code'. Must be one of the following valid purpose codes: BKFE, BKIP, BKPP, ...\n```\n\n**Purpose Code Categories:**\n- **Bank Debt**: `BKFE`, `BKIP`, `BKPP`\n- **Commercial**: `GDSV`, `SERV`, `SUPP`, `TRAD`, etc.\n- **Salary & Benefits**: `SALA`, `BONU`, `COMM`, `PENS`, etc.\n- **Tax**: `TAXS`, `INTX`, `VATX`, `HSTX`, etc.\n- **Utilities**: `ELEC`, `GASB`, `WATR`, `PHON`, etc.\n- **And many more categories...**\n\n```typescript\n// GBP FPS Payment (Faster Payments)\nconst fpsPayment = await bcb.payments.createGBPFPS({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_name: 'John Smith',\n  beneficiary_account_number: '12345678',\n  beneficiary_sort_code: '200000',\n  amount: '100.50',\n  reference: 'Invoice INV-2024-001',\n  reason: 'GDSV', // Purchase/Sale of Goods and Services\n  notes: 'Urgent payment'\n});\n\n// GBP CHAPS Payment (High Value)\nconst chapsPayment = await bcb.payments.createGBPCHAPS({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_name: 'Property Holdings Ltd',\n  beneficiary_account_number: '87654321',\n  beneficiary_sort_code: '400000',\n  beneficiary_address_line_1: '1 Property Square',\n  beneficiary_city: 'London',\n  beneficiary_postcode: 'E14 5AB', // Mandatory for CHAPS\n  beneficiary_country: 'GB',\n  beneficiary_lei_code: 'ABCDEFGHIJKLMNOP1234', // Mandatory for CHAPS\n  amount: '1500000.00',\n  reference: 'Property Purchase',\n  reason: 'HLRP' // Property Loan\n});\n\n// USD Payment\nconst usdPayment = await bcb.payments.createUSD({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_name: 'Tech Solutions Inc',\n  beneficiary_account_number: '123456789',\n  beneficiary_routing_number: '021000021',\n  beneficiary_address_line_1: '123 Silicon Valley Drive',\n  beneficiary_city: 'San Francisco',\n  beneficiary_country: 'US',\n  beneficiary_bank_name: 'Chase Bank',\n  beneficiary_bank_address: '270 Park Avenue, New York, NY 10017',\n  beneficiary_bank_country: 'US',\n  amount: '2500.00',\n  reference: 'Software License',\n  reason: 'SERV' // Service charges\n});\n\n// EUR Payment with IBAN\nconst eurPayment = await bcb.payments.createEUR({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_name: 'Deutsche Manufacturing GmbH',\n  beneficiary_address_line_1: 'Industriestraße 45',\n  beneficiary_city: 'Munich',\n  beneficiary_country: 'DE',\n  beneficiary_bic: 'DEUTDEFF',\n  beneficiary_iban: 'DE89370400440532013000',\n  amount: '750.00',\n  reference: 'Equipment Purchase',\n  reason: 'GDSV' // Purchase/Sale of Goods and Services\n});\n\n// International Payment (any currency)\nconst chfPayment = await bcb.payments.createInternational('CHF', {\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_name: 'Swiss Precision AG',\n  beneficiary_address_line_1: 'Bahnhofstrasse 123',\n  beneficiary_city: 'Zurich',\n  beneficiary_country: 'CH',\n  beneficiary_bic: 'UBSWCHZH80A',\n  beneficiary_account_number: 'CH9300762011623852957',\n  amount: '500.00',\n  reference: 'Consulting Services',\n  reason: 'SERV' // Service charges\n});\n\n// Payment with existing beneficiary\nconst beneficiaryPayment = await bcb.payments.authorize({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_account_id: 456,\n  ccy: 'GBP',\n  amount: '75.00',\n  reference: 'Monthly Payment',\n  reason: 'SERV', // Service charges\n  preferred_scheme: 'AUTO',\n  // V5 mandatory address fields\n  beneficiary_address_line_1: '123 Stored Address',\n  beneficiary_city: 'London',\n  beneficiary_country: 'GB'\n});\n\n// BLINC Network Payment\nconst blincPayment = await bcb.payments.createBLINC({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_account_id: 456,\n  ccy: 'BTC',\n  amount: '0.001',\n  reference: 'Crypto Transfer',\n  reason: 'TRAD', // Commercial transaction\n  beneficiary_address_line_1: 'Digital Network',\n  beneficiary_city: 'BLINC Network',\n  beneficiary_country: 'XX'\n});\n\n// Internal Transfer\nconst internalPayment = await bcb.payments.createInternal({\n  counterparty_id: 483,\n  sender_account_id: 378,\n  beneficiary_account_id: 379,\n  ccy: 'USD',\n  amount: '1000.00',\n  reference: 'Internal Transfer',\n  reason: 'ACCTCASH', // Account Management\n  beneficiary_address_line_1: 'Internal',\n  beneficiary_city: 'Same Institution',\n  beneficiary_country: 'XX'\n});\n```\n\n#### Payment Schemes\n\nThe V5 payments API supports three payment schemes:\n\n- **`AUTO`** - Automatic scheme selection for external payments\n- **`BLINC`** - Payments within the BLINC network (requires BLINC accounts)\n- **`INTERNAL`** - Payments between BCB accounts of the same counterparty\n\n#### Currency-Specific Requirements\n\nThe SDK automatically validates currency-specific requirements:\n\n**GBP Payments:**\n- **FPS**: Account number + sort code (under £1M)\n- **CHAPS**: Account number + sort code + full address + postcode + LEI code (over £1M)\n- **SWIFT**: Account number + sort code + SWIFT code (international)\n\n**USD Payments:**\n- Account number + routing number + bank name + bank address\n\n**EUR Payments:**\n- BIC + IBAN (or account number for non-IBAN countries)\n\n**Other Currencies:**\n- BIC + account number + full address\n\n#### V5 Address Fields (Mandatory)\n\nAll V5 payments require enhanced address fields:\n- `beneficiary_address_line_1` (mandatory)\n- `beneficiary_address_line_2` (optional)\n- `beneficiary_address_line_3` (optional)\n- `beneficiary_city` (mandatory)\n- `beneficiary_region` (optional)\n- `beneficiary_postcode` (mandatory for CHAPS)\n- `beneficiary_country` (mandatory)\n- `beneficiary_lei_code` (mandatory for CHAPS)\n\n#### Payment Response\n\n```typescript\ninterface PaymentResponse {\n  endToEndIdentification: string;  // Payment end-to-end ID\n  transactionId: string;           // BCB transaction ID\n  nonce?: string;                  // Optional nonce (if provided)\n  response: 'Accepted' | 'Rejected'; // Payment status\n}\n```\n\n#### Purpose Codes\n\nThe SDK supports all standard purpose codes for payment categorization. Common examples:\n- `GDSV` - Purchase Sale of Goods and Services\n- `SALA` - Salary Payment\n- `RENT` - Rent Payment\n- `INSU` - Insurance Premium\n- `TAXS` - Tax Payment\n- `DIVD` - Dividends\n\n#### Validation and Error Handling\n\nThe SDK provides comprehensive validation:\n- Required field validation\n- Currency-specific field validation\n- Amount format validation (decimal strings)\n- CHAPS-specific requirements (postcode, LEI code)\n- Scheme-specific requirements (BLINC, INTERNAL)\n\n```typescript\ntry {\n  const payment = await bcb.payments.authorize(paymentRequest);\n  console.log(`Payment ${payment.data[0].response}: ${payment.data[0].transactionId}`);\n} catch (error) {\n  if (error.message.includes('Amount must be')) {\n    console.error('Invalid amount format');\n  } else if (error.message.includes('CHAPS payments require')) {\n    console.error('Missing CHAPS requirements');\n  } else {\n    console.error('Payment failed:', error.message);\n  }\n}\n```\n\n### BLINC Network Management\n\nThe SDK provides comprehensive BLINC (Blockchain Interoperability Network for Commerce) functionality for managing connected BLINC members and beneficiaries:\n\n```typescript\n// List BLINC beneficiaries for a specific account\nconst blincBeneficiaries = await bcb.blinc.listBeneficiaries(9999);\nconsole.log(`Found ${blincBeneficiaries.data.length} BLINC beneficiaries`);\n\n// Filter BLINC beneficiaries by currency\nconst gbpBlincBeneficiaries = await bcb.blinc.listBeneficiariesByCurrency(9999, 'GBP');\nconst eurBlincBeneficiaries = await bcb.blinc.listBeneficiaries(9999, { ccy: 'EUR' });\n\n// Get a BLINC member by BLINC ID\nconst blincMember = await bcb.blinc.getMember('800999999999');\nconsole.log(`Member: ${blincMember.data[0].name}`);\n\n// Get single BLINC member (convenience method)\nconst singleMember = await bcb.blinc.getMemberSingle('800999999999');\nconsole.log(`Member: ${singleMember.data.name} (${singleMember.data.ccy})`);\n\n// Check if a BLINC member exists\nconst memberExists = await bcb.blinc.memberExists('800999999999');\nif (memberExists) {\n  console.log('BLINC member found');\n}\n\n// Get available currencies for BLINC beneficiaries\nconst currencies = await bcb.blinc.getAvailableCurrencies(9999);\nconsole.log(`Available currencies: ${currencies.join(', ')}`);\n\n// Search BLINC beneficiaries by name\nconst searchResults = await bcb.blinc.searchBeneficiariesByName(9999, 'John');\nconst filteredSearch = await bcb.blinc.searchBeneficiariesByName(9999, 'Smith', { ccy: 'GBP' });\n```\n\n#### BLINC Beneficiary Object Structure\n\n```typescript\ninterface BlincBeneficiary {\n  id: number;                    // Numeric internal account ID\n  blinc_id: string;              // Numeric BLINC account ID\n  ccy: string;                   // Currency of the BLINC account\n  name: string;                  // Name of the BLINC account holder\n}\n```\n\n#### BLINC Member Object Structure\n\n```typescript\ninterface BlincMember {\n  id: number;                    // Numeric internal account ID\n  blinc_id: string;              // Numeric BLINC account ID\n  ccy: string;                   // Currency of the BLINC account\n  name: string;                  // Name of the BLINC account holder\n}\n```\n\n#### BLINC Usage Examples\n\n```typescript\n// Get all BLINC beneficiaries for account analysis\nconst accounts = await bcb.accounts.list();\nconst firstAccountId = accounts.data[0].id;\n\nconst allBlincBeneficiaries = await bcb.blinc.listBeneficiaries(firstAccountId);\nconsole.log(`Total BLINC beneficiaries: ${allBlincBeneficiaries.data.length}`);\n\n// Group BLINC beneficiaries by currency\nconst byCurrency = allBlincBeneficiaries.data.reduce((acc, b) => {\n  acc[b.ccy] = (acc[b.ccy] || 0) + 1;\n  return acc;\n}, {} as Record<string, number>);\n\nconsole.log('BLINC beneficiaries by currency:', byCurrency);\n\n// Find specific BLINC members\nfor (const beneficiary of allBlincBeneficiaries.data) {\n  const memberDetail = await bcb.blinc.getMemberSingle(beneficiary.blinc_id);\n  console.log(`${memberDetail.data.name}: ${memberDetail.data.ccy}`);\n}\n\n// Currency-specific BLINC operations\nconst availableCurrencies = await bcb.blinc.getAvailableCurrencies(firstAccountId);\n\nfor (const currency of availableCurrencies) {\n  const currencyBeneficiaries = await bcb.blinc.listBeneficiariesByCurrency(firstAccountId, currency);\n  console.log(`${currency}: ${currencyBeneficiaries.data.length} BLINC beneficiaries`);\n}\n\n// Search and filter operations\nconst searchTerm = 'BLINC';\nconst searchResults = await bcb.blinc.searchBeneficiariesByName(firstAccountId, searchTerm);\nconsole.log(`Found ${searchResults.data.length} BLINC beneficiaries matching \"${searchTerm}\"`);\n\n// Combined search with currency filter\nconst gbpSearchResults = await bcb.blinc.searchBeneficiariesByName(\n  firstAccountId, \n  'Member', \n  { ccy: 'GBP' }\n);\nconsole.log(`GBP BLINC members containing \"Member\": ${gbpSearchResults.data.length}`);\n```\n\n#### BLINC Network Features\n\nThe BLINC functionality provides:\n\n- **Connected Members**: List all BLINC accounts connected to your entity\n- **Currency Filtering**: Filter BLINC beneficiaries by specific currencies\n- **Member Lookup**: Get detailed information about specific BLINC members\n- **Existence Checking**: Verify if a BLINC member exists before operations\n- **Name Search**: Search BLINC beneficiaries by name with partial matching\n- **Currency Discovery**: Get all available currencies for BLINC beneficiaries\n\n**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.\n\n### Beneficiary Creation (V4 API)\n\nThe 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:\n\n```typescript\n// Create a GBP beneficiary with Account Number + Sort Code\nconst gbpBeneficiary = await bcb.beneficiaries.createGBP({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"John Smith\",\n  host_name: \"Barclays Bank\",\n  sort_code: \"200000\",\n  account_number: \"12345678\",\n  node_name: \"John Smith\",\n  node_location_line_1: \"123 Main Street\",\n  node_location_city: \"London\",\n  node_location_postcode: \"SW1A 1AA\",\n  node_type: \"individual\",\n  email: \"john.smith@example.com\"\n});\n\n// Create a USD beneficiary with routing number\nconst usdBeneficiary = await bcb.beneficiaries.createUSD({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"Jane Doe\",\n  host_name: \"Bank of America\",\n  routing_number: \"121000358\",\n  account_number: \"987654321\",\n  node_name: \"Jane Doe\",\n  node_location_line_1: \"456 Wall Street\",\n  node_location_city: \"New York\",\n  node_location_postcode: \"10005\",\n  node_type: \"individual\",\n  email: \"jane.doe@example.com\"\n});\n\n// Create an IBAN beneficiary (EUR and other IBAN countries)\nconst eurBeneficiary = await bcb.beneficiaries.createIBAN({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"Marie Dubois\",\n  ccy: \"EUR\",\n  host_name: \"BNP Paribas\",\n  host_location: \"Paris, France\",\n  host_country: \"FR\",\n  node_name: \"Marie Dubois\",\n  node_location_line_1: \"456 Rue de Rivoli\",\n  node_location_city: \"Paris\",\n  node_location_postcode: \"75001\",\n  node_country: \"FR\",\n  node_type: \"individual\",\n  iban: \"FR1420041010050500013M02606\", // Valid French IBAN\n  bic: \"BNPAFRPPXXX\"\n});\n\n// Create a cryptocurrency wallet beneficiary\nconst btcBeneficiary = await bcb.beneficiaries.createWallet({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"Crypto Wallet\",\n  ccy: \"BTC\",\n  node_name: \"Bitcoin Wallet\",\n  wallet_address: \"3LxDswEWKC3mJewRYs44xr3qNBqXMwhszo\",\n  node_location_line_1: \"789 Tech Street\",\n  node_location_city: \"San Francisco\",\n  node_location_postcode: \"94105\",\n  node_country: \"US\",\n  node_type: \"individual\"\n});\n\n// Generic beneficiary creation with full control\nconst customBeneficiary = await bcb.beneficiaries.create({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"Custom Beneficiary\",\n  account_type: \"Bank\",\n  ccy: \"CAD\",\n  host_name: \"Royal Bank of Canada\",\n  host_hub: \"003200233\", // Canadian routing number\n  host_country: \"CA\",\n  node_name: \"Custom User\",\n  node_address: \"123456789\",\n  node_location_line_1: \"123 Maple Street\",\n  node_location_city: \"Toronto\",\n  node_location_postcode: \"M5V 3A8\",\n  node_country: \"CA\",\n  node_type: \"individual\",\n  bic: \"ROYCCAT2XXX\",\n  account_label: \"Custom User - CAD\",\n  email: \"custom.user@example.com\",\n  bcb_controlled: 1,\n  is_beneficiary: 1\n});\n```\n\n#### Beneficiary Creation Convenience Methods\n\nThe beneficiaries service provides country and currency-specific convenience methods:\n\n- **`bcb.beneficiaries.createGBP()`** - For UK bank accounts with sort codes\n- **`bcb.beneficiaries.createUSD()`** - For US bank accounts with routing numbers  \n- **`bcb.beneficiaries.createIBAN()`** - For IBAN-based bank accounts (EUR and other IBAN countries)\n- **`bcb.beneficiaries.createWallet()`** - For cryptocurrency wallet addresses\n- **`bcb.beneficiaries.create()`** - Generic method with full control over all parameters\n\n#### Validation and Requirements\n\nThe SDK automatically validates beneficiary creation requests based on:\n\n- **Account Type**: Bank accounts require either IBAN or account number\n- **Currency Requirements**: USD in US requires routing number, GBP in UK requires sort code\n- **Address Requirements**: GBP accounts require full address for CHAPS payments\n- **Node Type**: Must be either \"individual\" or \"corporate\"\n- **Required Flags**: `bcb_controlled` and `is_beneficiary` must be 1\n\n#### Create Beneficiary Response\n\n```typescript\ninterface CreateBeneficiaryResponse {\n  id: number;  // Unique beneficiary ID of the created account\n}\n```\n\n**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.\n\n#### Country-Specific Examples\n\n```typescript\n// UK GBP Account with Sort Code\nconst ukAccount = await bcb.beneficiaries.createGBP({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"UK Beneficiary\",\n  host_name: \"HSBC\",\n  sort_code: \"400000\", // UK sort code\n  account_number: \"12345678\",\n  node_name: \"John Smith\",\n  node_location_line_1: \"123 High Street\",\n  node_location_city: \"London\",\n  node_location_postcode: \"SW1A 1AA\", // Required for CHAPS\n  node_type: \"individual\"\n});\n\n// US USD Account with Routing Number\nconst usAccount = await bcb.beneficiaries.createUSD({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"US Beneficiary\",\n  host_name: \"Chase Bank\",\n  routing_number: \"021000021\", // US routing number\n  account_number: \"123456789\",\n  node_name: \"Jane Doe\",\n  node_location_line_1: \"456 Broadway\",\n  node_location_city: \"New York\",\n  node_location_postcode: \"10013\",\n  node_type: \"individual\"\n});\n\n// EU EUR Account with IBAN\nconst euAccount = await bcb.beneficiaries.createIBAN({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"EU Beneficiary\",\n  ccy: \"EUR\",\n  host_name: \"Deutsche Bank\",\n  host_country: \"DE\",\n  node_name: \"Hans Mueller\",\n  node_location_line_1: \"Hauptstraße 123\",\n  node_location_city: \"Frankfurt\",\n  node_location_postcode: \"60311\",\n  node_country: \"DE\",\n  node_type: \"individual\",\n  iban: \"DE89370400440532013000\", // German IBAN\n  bic: \"DEUTDEFFXXX\"\n});\n\n// Bitcoin Wallet\nconst btcWallet = await bcb.beneficiaries.createWallet({\n  counterparty_id: 483,\n  cid: \"CX00000101\",\n  name: \"BTC Wallet\",\n  ccy: \"BTC\",\n  node_name: \"Bitcoin Wallet\",\n  wallet_address: \"3LxDswEWKC3mJewRYs44xr3qNBqXMwhszo\",\n  node_location_line_1: \"789 Crypto Street\",\n  node_location_city: \"San Francisco\",\n  node_location_postcode: \"94105\",\n  node_country: \"US\",\n  node_type: \"individual\"\n});\n```\n\n### VOP (Verification of Payee) Management\n\nThe 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.\n\n**Note**: VOP is part of the payments module and uses the Client API endpoint (`client-api.bcb.group`) rather than the standard API endpoint.\n\n```typescript\n// Approve a payment for verification of payee\nconst approveResult = await bcb.payments.vop.approve(123, 'E2E123456789');\nconsole.log(`Payment approved: ${approveResult.status}`); // 202 - Accepted\n\n// Cancel a payment for verification of payee\nconst cancelResult = await bcb.payments.vop.cancel(123, 'E2E987654321');\nconsole.log(`Payment cancelled: ${cancelResult.status}`); // 202 - Accepted\n```\n\n#### VOP Methods\n\n```typescript\n// Approve unverified payment\nconst approveResult = await bcb.payments.vop.approve(accountId, e2eId);\n\n// Cancel unverified payment\nconst cancelResult = await bcb.payments.vop.cancel(accountId, e2eId);\n```\n\n#### VOP Response Codes\n\nThe VOP endpoints return specific HTTP status codes:\n\n- **202** - Success (payment approved/cancelled)\n- **401** - Unauthorized (invalid credentials)\n- **404** - No unverified payment found for the account with the given end-to-end ID\n- **409** - The payment is not unverified (already processed)\n\n#### Error Handling\n\n```typescript\ntry {\n  const result = await bcb.payments.vop.approve(123, 'E2E123456789');\n  console.log('Payment approved successfully');\n} catch (error) {\n  if (error.message.includes('404')) {\n    console.log('No unverified payment found for this account with the given end-to-end ID');\n  } else if (error.message.includes('409')) {\n    console.log('The payment is not unverified');\n  } else if (error.message.includes('401')) {\n    console.log('Unauthorized - check your credentials');\n  } else {\n    console.error('VOP operation failed:', error.message);\n  }\n}\n```\n\n#### Batch VOP Operations\n\n```typescript\n// Process multiple VOP approvals\nconst payments = [\n  { accountId: 123, e2eId: 'E2E111111111' },\n  { accountId: 123, e2eId: 'E2E222222222' },\n  { accountId: 456, e2eId: 'E2E333333333' },\n];\n\nconst results = await Promise.allSettled(\n  payments.map(payment => \n    bcb.payments.vop.approve(payment.accountId, payment.e2eId)\n  )\n);\n\nresults.forEach((result, index) => {\n  const payment = payments[index];\n  if (result.status === 'fulfilled') {\n    console.log(`✅ Payment ${payment.e2eId} approved successfully`);\n  } else {\n    console.log(`❌ Payment ${payment.e2eId} failed:`, result.reason.message);\n  }\n});\n```\n\n#### VOP with Retry Logic\n\n```typescript\nconst retryApprove = async (accountId: number, e2eId: string, maxRetries = 3) => {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    try {\n      const result = await bcb.payments.vop.approve(accountId, e2eId);\n      console.log(`✅ Payment approved on attempt ${attempt}`);\n      return result;\n    } catch (error) {\n      console.log(`❌ Attempt ${attempt} failed:`, error.message);\n      \n      if (attempt === maxRetries) {\n        throw error;\n      }\n      \n      // Wait before retry (exponential backoff)\n      const delay = Math.pow(2, attempt) * 1000;\n      console.log(`⏳ Waiting ${delay}ms before retry...`);\n      await new Promise(resolve => setTimeout(resolve, delay));\n    }\n  }\n};\n\ntry {\n  await retryApprove(123, 'E2E444444444');\n} catch (error) {\n  console.log('❌ All retry attempts failed:', error.message);\n}\n```\n\n### Virtual Accounts Management (V2 Client API)\n\nThe 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).\n\n**Note**: Virtual accounts use the Client API endpoint (`client-api.bcb.group`) rather than the standard API endpoint.\n\n```typescript\n// Create mixed EUR and GBP virtual accounts\nconst mixedOwners = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440001\", // UUID format required\n    name: \"Hans Mueller\",\n    addressLine1: \"Friedrichstrasse\",\n    addressLine2: \"100\",\n    city: \"Berlin\",\n    region: \"Berlin\",\n    postcode: \"10115\",\n    country: \"DE\",\n    nationality: \"DE\",\n    dateOfBirth: \"1985-03-15\",\n    isIndividual: true,\n    iban: \"DE53202208000099106929\",\n    bicSwift: \"MHSBDEHBXXX\"\n  },\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440002\", // UUID format required\n    name: \"ACME Trading Ltd\",\n    addressLine1: \"One London Wall\",\n    addressLine2: \"6th Floor\",\n    city: \"London\",\n    region: \"Greater London\",\n    postcode: \"EC2Y 5EB\",\n    country: \"GB\",\n    nationality: \"GB\",\n    registrationNumber: \"AB123456\",\n    isIndividual: false,\n    accountNumber: \"55555555\",\n    sortCode: \"202015\"\n  }\n];\n\nconst segregatedAccountId = 123; // Your segregated account ID\nconst response = await bcb.virtualAccounts.create(segregatedAccountId, {\n  owners: mixedOwners\n});\n\nconsole.log(`Status: ${response.status}`); // 202 - Accepted\nconsole.log(`Response: ${JSON.stringify(response.data)}`);\n```\n\n#### Convenience Methods\n\nThe virtual accounts service provides convenient methods for specific use cases:\n\n```typescript\n// Create virtual accounts for individuals only\nconst individuals = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440003\", // UUID format required\n    name: \"Alice Johnson\",\n    addressLine1: \"123 High Street\",\n    city: \"Manchester\",\n    postcode: \"M1 1AA\",\n    country: \"GB\",\n    nationality: \"GB\",\n    dateOfBirth: \"1988-11-03\",\n    accountNumber: \"12345678\",\n    sortCode: \"123456\"\n  }\n];\n\nconst individualResponse = await bcb.virtualAccounts.createForIndividuals(\n  segregatedAccountId,\n  individuals\n);\n\n// Create virtual accounts for companies only\nconst companies = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440004\", // UUID format required\n    name: \"Digital Solutions Ltd\",\n    addressLine1: \"Tech Park\",\n    addressLine2: \"Building A\",\n    city: \"Cambridge\",\n    postcode: \"CB1 2AB\",\n    country: \"GB\",\n    nationality: \"GB\",\n    registrationNumber: \"12345678\",\n    accountNumber: \"87654321\",\n    sortCode: \"654321\"\n  }\n];\n\nconst companyResponse = await bcb.virtualAccounts.createForCompanies(\n  segregatedAccountId,\n  companies\n);\n\n// Create virtual accounts without bank details (receive-only)\nconst noBankDetails = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440005\", // UUID format required\n    name: \"Startup Company Ltd\",\n    addressLine1: \"Innovation Hub\",\n    city: \"Bristol\",\n    postcode: \"BS1 1AA\",\n    country: \"GB\",\n    nationality: \"GB\",\n    registrationNumber: \"STARTUP123\",\n    isIndividual: false\n  }\n];\n\nconst noBankResponse = await bcb.virtualAccounts.createWithoutBankDetails(\n  segregatedAccountId,\n  noBankDetails\n);\n\n// Currency-specific convenience methods\nconst gbpOwners = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440006\", // UUID format required\n    name: \"GBP Account Holder\",\n    addressLine1: \"London Street\",\n    city: \"London\",\n    postcode: \"SW1A 1AA\",\n    country: \"GB\",\n    nationality: \"GB\",\n    dateOfBirth: \"1990-01-01\",\n    isIndividual: true,\n    accountNumber: \"11111111\",\n    sortCode: \"111111\"\n  }\n];\n\nconst gbpResponse = await bcb.virtualAccounts.createGBP(segregatedAccountId, gbpOwners);\n\nconst eurOwners = [\n  {\n    correlationId: \"550e8400-e29b-41d4-a716-446655440007\", // UUID format required\n    name: \"EUR Account Holder\",\n    addressLine1: \"Berlin Strasse\",\n    city: \"Berlin\",\n    postcode: \"10115\",\n    country: \"DE\",\n    nationality: \"DE\",\n    registrationNumber: \"DE123456\",\n    isIndividual: false,\n    iban: \"DE89370400440532013000\",\n    bicSwift: \"COBADEFFXXX\"\n  }\n];\n\nconst eurResponse = await bcb.virtualAccounts.createEUR(segregatedAccountId, eurOwners);\n```\n\n#### Virtual Account Owner Object Structure\n\n```typescript\ninterface VirtualAccountOwner {\n  correlationId: string;          // Unique identifier for this owner (must be valid UUID format)\n  name: string;                   // Legal name of the account holder\n  addressLine1: string;           // Primary address line (mandatory)\n  addressLine2?: string;          // Secondary address line (optional)\n  addressLine3?: string;          // Tertiary address line (optional)\n  city: string;                   // City (mandatory)\n  region?: string;                // Region/state (optional)\n  postcode: string;               // Postal/ZIP code (mandatory)\n  country: string;                // 2-letter country code (mandatory)\n  nationality: string;            // 2-letter nationality code (mandatory)\n  isIndividual: boolean;          // true for individuals, false for companies\n  \n  // For individuals only\n  dateOfBirth?: string;           // YYYY-MM-DD format (required for individuals)\n  \n  // For companies only\n  registrationNumber?: string;    // Company registration number (required for companies)\n  \n  // Bank details (optional - can create without for receive-only accounts)\n  iban?: string;                  // International Bank Account Number\n  bicSwift?: string;              // Bank Identifier Code / SWIFT code\n  \n  // GBP specific fields\n  accountNumber?: string;         // 8-digit UK account number\n  sortCode?: string;              // 6-digit UK sort code\n}\n```\n\n#### Virtual Accounts Features\n\n- **Mixed Owner Types**: Create accounts for both individuals and companies in a single request\n- **Currency Support**: EUR (IBAN/BIC) and GBP (sort code/account number) with automatic validation\n- **Receive-Only Accounts**: Create accounts without bank details for incoming payments only\n- **Bulk Creation**: Up to 1000 owners per request for efficient bulk operations\n- **Account Listing**: List and search virtual accounts with pagination support\n- **Account Closure**: Close virtual accounts by IBAN when no longer needed\n- **Complete Lifecycle**: Create → List → Find virtual IBAN → Close workflow\n- **Comprehensive Validation**: Client-side validation of all required fields and formats\n- **Convenience Methods**: Currency and type-specific methods for common use cases\n\n#### Requirements and Validation\n\n**Individual Accounts:**\n- `dateOfBirth` is required (YYYY-MM-DD format)\n- `registrationNumber` should not be provided\n\n**Company Accounts:**\n- `registrationNumber` is required\n- `dateOfBirth` should not be provided\n\n**GBP Bank Details:**\n- `accountNumber` must be 8 digits\n- `sortCode` must be 6 digits\n- Both are required together or both omitted\n\n**EUR Bank Details:**\n- `iban` must be valid IBAN format (2 letters + 2 digits + up to 30 alphanumeric)\n- `bicSwift` must be valid BIC format (8 or 11 characters)\n\n**Address Requirements:**\n- `country` and `nationality` must be 2-letter ISO country codes\n- `addressLine1`, `city`, and `postcode` are mandatory\n- `addressLine2`, `addressLine3`, and `region` are optional\n\n#### Error Handling\n\nThe SDK provides comprehensive validation before making API calls:\n\n```typescript\ntry {\n  const response = await bcb.virtualAccounts.create(accountId, { owners });\n  console.log(`Created ${owners.length} virtual accounts successfully`);\n} catch (error) {\n  if (error.message.includes('correlationId is required')) {\n    console.error('Missing correlation ID');\n  } else if (error.message.includes('Maximum 1000 owners allowed')) {\n    console.error('Too many owners in request');\n  } else if (error.message.includes('dateOfBirth is required for individuals')) {\n    console.error('Individual missing date of birth');\n  } else if (error.status === 404) {\n    console.error('Segregated account not found');\n  } else {\n    console.error('Virtual account creation failed:', error.message);\n  }\n}\n\n// Close a virtual account\ntry {\n  const closeResponse = await bcb.virtualAccounts.close(accountId, \"GB33BUKB20201555555555\");\n  console.log('Virtual account closed successfully');\n} catch (error) {\n  if (error.status === 404) {\n    console.error('Virtual account or IBAN not found');\n  } else {\n    console.error('Virtual account closure failed:', error.message);\n  }\n}\n```\n\n### Event Monitoring\n\n```typescript\n// Monitor requests and responses\nbcb.on('request', ({ request }) => {\n  console.log('Request:', request.method, request.url);\n});\n\nbcb.on('response', ({ response }) => {\n  console.log('Response:', response.status, response.requestId);\n});\n\nbcb.on('error', ({ error }) => {\n  console.error('Error:', error.message);\n});\n```\n\n## 🔧 Development\n\n### Building\n\n```bash\nnpm run build       # Build both CJS and ESM\nnpm run build:cjs   # CommonJS build\nnpm run build:esm   # ES Modules build\n```\n\n### Code Quality\n\n```bash\nnpm run lint        # ESLint\nnpm run lint:fix    # Fix linting issues\nnpm run format      # Prettier formatting\nnpm run typecheck       # TypeScript checking for src/ (also runs in CI before build)\nnpm run typecheck:test  # TypeScript checking for tests/ + src/\n```\n\n### Testing Development\n\nThe testing infrastructure is designed to be **highly expandable**:\n\n#### Adding New Tests\n\n```typescript\n// tests/acceptance/my-feature.test.ts\nimport { createTestContext, skipIfConfigInvalid } from '../helpers/test-utils';\n\ndescribe('My Feature Tests', () => {\n  let testContext: ReturnType<typeof createTestContext>;\n\n  beforeEach(() => {\n    if (skipIfConfigInvalid()) return;\n    testContext = createTestContext({ test: 'my-feature' });\n  });\n\n  it('should test real API functionality', async () => {\n    if (skipIfConfigInvalid()) {\n      console.log('⏭️  Skipping: No valid test configuration');\n      return;\n    }\n\n    const { sdk } = testContext;\n    // Your test code here - runs against real API\n  });\n});\n```\n\n#### Custom Test Utilities\n\n```typescript\nimport { TestDataGenerator, retry, wait } from '../helpers/test-utils';\n\n// Generate test data\nconst testUser = TestDataGenerator.testObject({\n  email: TestDataGenerator.randomEmail()\n});\n\n// Retry with backoff\nconst result = await retry(() => sdk.get('/endpoint'), 3);\n\n// Rate limiting\nawait wait(1000);\n```\n\n#### Performance Testing\n\n```typescript\nimport { testUtils } from '../helpers/test-utils';\n\ntestUtils.performance.start('operation');\nawait sdk.post('/endpoint', data);\nconst duration = testUtils.performance.end('operation');\n\nexpect(duration).toBeLessThan(5000);\n```\n\n### Test Environment Settings\n\nThe framework automatically adapts to different environments:\n\n- **Development**: Lenient timeouts, detailed logging\n- **Staging**: Production-like settings, real data validation  \n- **Production**: Conservative timeouts, minimal test impact\n\n## 📊 Test Reports\n\nTests generate comprehensive reports including:\n\n- ✅ **Pass/Fail Status** - Detailed test results\n- ⏱️ **Performance Metrics** - Request timing and throughput\n- 🔍 **Error Analysis** - Real API error response validation\n- 📈 **Coverage Reports** - Code coverage analysis\n- 🌍 **Environment Info** - Test configuration and context\n\n## 🔗 API Compatibility\n\nBased on the BCB API OpenAPI specification:\n\n### Authentication Endpoint\n- **POST** `/auth/oauth/token` - OAuth2 client credentials flow\n\n### Accounts Endpoints\n- **GET** `/v3/accounts` - List accounts with optional filtering and pagination\n  - Query Parameters:\n    - `counterparty_id` - Filter by counterparty ID\n    - `limit` - Limit result set for server-side paging\n    - `pageToken` - Offset result set when paging\n    - `id` - Return single result with matching account ID\n\n### Balances Endpoints\n- **GET** `/v3/balances/{account_id}` - Get balance for a specific account\n  - Path Parameters:\n    - `account_id` - Account ID (numeric or string)\n  - Query Parameters:\n    - `dateTo` - Get end-of-day balance for specific date (YYYY-MM-DD format)\n\n### Transactions Endpoints\n- **GET** `/v3/accounts/{account_id}/transactions` - List transactions for a specific account\n  - Path Parameters:\n    - `account_id` - Account ID (numeric only)\n  - Query Parameters:\n    - `dateFrom` - Start date for filtering (YYYY-MM-DD format)\n    - `dateTo` - End date for filtering (YYYY-MM-DD format)\n    - `limit` - Limit result set for server-side paging\n    - `pageToken` - Offset result set when paging\n\n- **GET** `/v3/accounts/{account_id}/transactions/{tx_id}` - Get transaction detail by ID\n  - Path Parameters:\n    - `account_id` - Account ID (numeric or string)\n    - `tx_id` - Transaction ID (unique code)\n\n### Beneficiaries Endpoints\n- **GET** `/v3/beneficiaries` - List beneficiaries with optional filtering and pagination\n  - Query Parameters:\n    - `limit` - Limit result set for server-side paging\n    - `pageToken` - Offset result set when paging\n\n### BLINC Network Endpoints\n- **GET** `/v3/accounts/{account_id}/blinc-beneficiaries` - List BLINC beneficiaries for an account\n  - Path Parameters:\n    - `account_id` - BLINC account ID of counterparty requesting connected BLINC members list\n  - Query Parameters:\n    - `ccy` - Restrict response to BLINC accounts with matching currency (ISO 4217 format)\n\n- **GET** `/v3/blinc-accounts/{blinc_id}` - Get BLINC member by BLINC ID\n  - Path Parameters:\n    - `blinc_id` - BLINC ID of another BLINC member (numeric)\n\n### Payments Endpoints (V5 API)\n- **POST** `/v5/payments/authorise` - Authorize payment with enhanced address fields (V5)\n  - Request Body: PaymentRequest object with:\n    - Required: `counterparty_id`, `sender_account_id`, `ccy`, `amount`, `reference`, `reason`, `preferred_scheme`\n    - V5 Mandatory Address: `beneficiary_address_line_1`, `beneficiary_city`, `beneficiary_country`\n    - Beneficiary: Either `beneficiary_account_id` OR beneficiary details (`beneficiary_name`, banking details)\n    - Currency Specific: GBP requires sort code, USD requires routing number, EUR requires BIC\n    - CHAPS Specific: `beneficiary_postcode` and `beneficiary_lei_code` mandatory for high-value GBP payments\n    - Schemes: `AUTO` (external), `BLINC` (network), `INTERNAL` (same counterparty)\n\n### Beneficiary Creation Endpoints (V4 API)\n- **POST** `/v4/accounts` - Create beneficiary account with enhanced address fields\n  - Request Body: CreateBeneficiaryRequest object with:\n    - Required: `counterparty_id`, `cid`, `name`, `account_type`, `ccy`, `node_name`, `node_type`, `bcb_controlled: 1`, `is_beneficiary: 1`\n    - Account Type Specific: Bank accounts require IBAN or account number, wallets require wallet address\n    - Address Fields: Enhanced V4 address structure with separate fields for line 1-3, city, region, postcode\n    - Currency Specific: USD in US requires routing number, GBP in UK requires sort code and full address for CHAPS\n\n### VOP (Verification of Payee) Endpoints (Client API)\n- **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\n  - Path Parameters:\n    - `accountId` - The account ID (numeric)\n    - `e2eId` - The end-to-end ID of the payment (string)\n  - Response: 202 (Accepted), 401 (Unauthorized), 404 (No unverified payment found), 409 (Payment is not unverified)\n  - **Note**: Uses Client API endpoint (`client-api.bcb.group`)\n\n- **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\n  - Path Parameters:\n    - `accountId` - The account ID (numeric)\n    - `e2eId` - The end-to-end ID of the payment (string)\n  - Response: 202 (Accepted), 401 (Unauthorized), 404 (No unverified payment found), 409 (Payment is not unverified)\n  - **Note**: Uses Client API endpoint (`client-api.bcb.group`)\n\n### Virtual Accounts Endpoints (Client API)\n- **POST** `/v2/accounts/{accountId}/virtual` - Create virtual accounts for given owners (V2 Client API endpoint)\n  - Path Parameters:\n    - `accountId` - The account id for the segregated account (numeric)\n  - Request Body: Array of VirtualAccountOwner objects with:\n    - Required: `correlationId` (UUID format), `name`, `addressLine1`, `city`, `postcode`, `country`, `nationality`, `isIndividual`\n    - Individual Specific: `dateOfBirth` (YYYY-MM-DD format) required for `isIndividual: true`\n    - Company Specific: `registrationNumber` required for `isIndividual: false`\n    - Optional Bank Details: `iban` + `bicSwift` for EUR, `accountNumber` + `sortCode` for GBP (owner's external bank details)\n    - Address Fields: `addressLine2`, `addressLine3`, `region` optional\n    - Limits: Maximum 1000 owners per request\n\n- **GET** `/v1/accounts/{accountId}/virtual/all-account-data` - List virtual accounts (V1 Client API endpoint)\n  - Path Parameters:\n    - `accountId` - The account id for the segregated account (numeric)\n  - Query Parameters:\n    - `pageIndex` - The page offset (optional, default: 0)\n    - `pageSize` - The limit of results (optional, default: 1000)\n  - Response: Virtual accounts with their generated IBANs and status\n\n- **POST** `/v1/accounts/{accountId}/virtual/{iban}/close` - Close virtual account by IBAN (V1 Client API endpoint)\n  - Path Parameters:\n    - `accountId` - The account id for the segregated account (numeric)\n    - `iban` - The IBAN of the virtual account to close (generated virtual IBAN, not owner's external IBAN)\n  - Response: 202 (Accepted) or 404 (Account/IBAN not found)\n\n### Response Format\nAll API responses follow the standard BCB format with:\n- `data` - Response payload\n- `status` - HTTP status code  \n- `headers` - Response headers\n- `requestId` - Unique request identifier\n\n## 🛡️ Security\n\n- ✅ Credentials stored securely in environment variables\n- ✅ No hardcoded secrets in code\n- ✅ Token automatic refresh and caching\n- ✅ Proper error handling without data leakage\n\n## 📚 Additional Resources\n\n- **[Testing Guide](./TESTING.md)** - Comprehensive testing documentation\n- **[Accounts Usage Example](./examples/accounts-usage.ts)** - Complete accounts functionality demo\n- **[Balances Usage Example](./examples/balances-usage.ts)** - Complete balances functionality demo\n- **[Transactions Usage Example](./examples/transactions-usage.ts)** - Complete transactions functionality demo\n- **[Payments Usage Example](./examples/payments-usage.ts)** - Complete payments functionality demo\n- **[VOP Usage Example](./examples/vop-usage.ts)** - Complete VOP functionality demo\n- **[Virtual Accounts Usage Example](./examples/virtual-accounts-usage.ts)** - Complete virtual accounts functionality demo\n- **[BCB API Documentation](https://docs.bcb.group)** - Official API docs\n- **[TypeScript Guide](https://www.typescriptlang.org/)** - TypeScript documentation\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass: `npm test`\n5. Submit a pull request\n\n## 📄 License\n\nMIT License - see LICENSE file for details.\n\n---\n\n**Built with ❤️ for developers who need reliable, well-tested SDKs that work against real APIs.**","readmeFilename":"README.md"}