Juergen Kunz e040e202cf 3.0.0
2025-07-18 12:31:43 +00:00
2025-07-18 10:31:12 +00:00
2025-07-18 10:31:12 +00:00
2020-06-20 01:47:53 +00:00
2019-09-26 12:07:56 +02:00
2025-07-18 10:31:12 +00:00
2025-07-18 12:31:43 +00:00
2025-07-18 12:31:43 +00:00
2025-07-18 10:43:39 +00:00
2019-10-02 23:34:05 +02:00
2025-07-18 11:42:06 +00:00
2025-07-18 10:31:12 +00:00
2025-07-18 10:43:39 +00:00

@apiclient.xyz/bunq

A powerful, type-safe TypeScript/JavaScript client for the bunq API with full feature coverage

Features

Core Banking Operations

  • 💳 Complete Account Management - Access all account types (personal, business, joint)
  • 💸 Advanced Payment Processing - Single payments, batch payments, scheduled payments
  • 📊 Transaction History - Full transaction access with filtering and pagination
  • 💰 Payment Requests - Send and manage payment requests with bunq.me integration
  • 📝 Draft Payments - Create payments requiring approval

Advanced Features

  • 🔄 Automatic Session Management - Handles token refresh and session renewal
  • 🔐 Full Security Implementation - Request signing and response verification
  • 🎯 Webhook Support - Real-time notifications with signature verification
  • 💳 Card Management - Full card control (activation, limits, blocking)
  • 📎 File Attachments - Upload and attach files to payments
  • 📑 Statement Exports - Export statements in multiple formats (PDF, CSV, MT940)
  • 🔗 OAuth Support - Third-party app integration
  • 🧪 Sandbox Environment - Full testing support

Developer Experience

  • 📘 Full TypeScript Support - Complete type definitions for all API responses
  • 🏗️ Builder Pattern APIs - Intuitive payment and request builders
  • Promise-based - Modern async/await support throughout
  • 🛡️ Type Safety - Compile-time type checking for all operations
  • 📚 Comprehensive Documentation - Detailed examples for every feature

Installation

npm install @apiclient.xyz/bunq
yarn add @apiclient.xyz/bunq
pnpm add @apiclient.xyz/bunq

Quick Start

import { BunqAccount } from '@apiclient.xyz/bunq';

// Initialize the client
const bunq = new BunqAccount({
  apiKey: 'your-api-key',
  deviceName: 'My App',
  environment: 'PRODUCTION' // or 'SANDBOX' for testing
});

// Initialize connection
await bunq.init();

// Get your accounts
const accounts = await bunq.getAccounts();
console.log(`Found ${accounts.length} accounts`);

// Get recent transactions
const transactions = await accounts[0].getTransactions();
transactions.forEach(tx => {
  console.log(`${tx.created}: ${tx.amount.value} ${tx.amount.currency} - ${tx.description}`);
});

// Always cleanup when done
await bunq.stop();

Core Examples

Account Management

// Get all accounts with details
const accounts = await bunq.getAccounts();

for (const account of accounts) {
  console.log(`Account: ${account.description}`);
  console.log(`Balance: ${account.balance.value} ${account.balance.currency}`);
  console.log(`IBAN: ${account.iban}`);
  
  // Get account-specific transactions
  const transactions = await account.getTransactions({
    count: 50,  // Last 50 transactions
    newer_id: false,
    older_id: false
  });
}

// Create a new monetary account (business accounts only)
const newAccount = await BunqMonetaryAccount.create(bunq, {
  currency: 'EUR',
  description: 'Savings Account',
  dailyLimit: '1000.00',
  overdraftLimit: '0.00'
});

Making Payments

Simple Payment

// Using the payment builder pattern
const payment = await BunqPayment.builder(bunq, account)
  .amount('25.00', 'EUR')
  .toIban('NL91ABNA0417164300', 'John Doe')
  .description('Birthday gift')
  .create();

console.log(`Payment created with ID: ${payment.id}`);

Payment with Custom Request ID (Idempotency)

// Prevent duplicate payments with custom request IDs
const payment = await BunqPayment.builder(bunq, account)
  .amount('100.00', 'EUR')
  .toIban('NL91ABNA0417164300', 'Supplier B.V.')
  .description('Invoice #12345')
  .customRequestId('invoice-12345-payment')  // Prevents duplicate payments
  .create();

Batch Payments

const batch = new BunqPaymentBatch(bunq);

// Create multiple payments in one API call
const batchId = await batch.create(account, [
  {
    amount: { value: '10.00', currency: 'EUR' },
    counterparty_alias: { 
      type: 'IBAN', 
      value: 'NL91ABNA0417164300', 
      name: 'Employee 1' 
    },
    description: 'Salary payment'
  },
  {
    amount: { value: '20.00', currency: 'EUR' },
    counterparty_alias: { 
      type: 'EMAIL', 
      value: 'freelancer@example.com', 
      name: 'Freelancer' 
    },
    description: 'Project payment'
  }
]);

// Check batch status
const batchDetails = await batch.get(account, batchId);
console.log(`Batch status: ${batchDetails.status}`);
console.log(`Total amount: ${batchDetails.total_amount.value}`);

Scheduled & Recurring Payments

const scheduler = new BunqSchedulePayment(bunq);

// One-time scheduled payment
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);

const scheduledId = await BunqSchedulePayment.builder(bunq, account)
  .amount('50.00', 'EUR')
  .toIban('NL91ABNA0417164300', 'Landlord')
  .description('Rent payment')
  .scheduleOnce(tomorrow.toISOString())
  .create();

// Recurring monthly payment
const recurringId = await BunqSchedulePayment.builder(bunq, account)
  .amount('9.99', 'EUR')
  .toIban('NL91ABNA0417164300', 'Netflix B.V.')
  .description('Monthly subscription')
  .scheduleMonthly('2024-01-01T10:00:00Z', '2024-12-31T10:00:00Z')
  .create();

// List all scheduled payments
const schedules = await scheduler.list(account);

// Cancel a scheduled payment
await scheduler.delete(account, scheduledId);

Payment Requests

// Create a payment request
const request = await BunqRequestInquiry.builder(bunq, account)
  .amount('25.00', 'EUR')
  .fromEmail('friend@example.com', 'My Friend')
  .description('Lunch money')
  .allowBunqme()  // Generate bunq.me link
  .minimumAge(18)
  .create();

console.log(`Share this link: ${request.bunqmeShareUrl}`);

// List pending requests
const requests = await BunqRequestInquiry.list(bunq, account.id);
const pending = requests.filter(r => r.status === 'PENDING');

// Cancel a request
await request.update(requestId, { status: 'CANCELLED' });

Draft Payments (Requires Approval)

const draft = new BunqDraftPayment(bunq, account);

// Create a draft with multiple payments
const draftId = await draft.create({
  numberOfRequiredAccepts: 2,  // Requires 2 approvals
  entries: [
    {
      amount: { value: '1000.00', currency: 'EUR' },
      counterparty_alias: { 
        type: 'IBAN', 
        value: 'NL91ABNA0417164300', 
        name: 'Supplier A' 
      },
      description: 'Invoice payment'
    },
    {
      amount: { value: '2000.00', currency: 'EUR' },
      counterparty_alias: { 
        type: 'IBAN', 
        value: 'NL91ABNA0417164300', 
        name: 'Supplier B' 
      },
      description: 'Equipment purchase'
    }
  ]
});

// Approve the draft
await draft.accept();

// Or reject it
await draft.reject('Budget exceeded');

Card Management

// List all cards
const cards = await BunqCard.list(bunq);

// Activate a new card
const card = cards.find(c => c.status === 'INACTIVE');
if (card) {
  await card.activate('123456');  // Activation code
}

// Update spending limits
await card.updateLimit('500.00', 'EUR');

// Update PIN
await card.updatePin('1234', '5678');

// Block a card
await card.block('LOST');

// Set country permissions
await card.setCountryPermissions([
  { country: 'NL', expiry_time: '2025-01-01T00:00:00Z' },
  { country: 'BE', expiry_time: '2025-01-01T00:00:00Z' }
]);

// Order a new card
const newCard = await BunqCard.order(bunq, {
  type: 'MASTERCARD',
  subType: 'PHYSICAL',
  nameOnCard: 'JOHN DOE',
  secondLine: 'Travel Card',
  monetaryAccountId: account.id
});

Webhooks

// Setup webhook server
const webhookServer = new BunqWebhookServer(bunq, {
  port: 3000,
  publicUrl: 'https://myapp.com/webhooks'
});

// Register event handlers
webhookServer.getHandler().onPayment((payment) => {
  console.log(`New payment: ${payment.amount.value} ${payment.amount.currency}`);
  console.log(`From: ${payment.counterparty_alias.display_name}`);
  console.log(`Description: ${payment.description}`);
  
  // Your business logic here
  updateDatabase(payment);
  sendNotification(payment);
});

webhookServer.getHandler().onRequest((request) => {
  console.log(`New payment request: ${request.amount_inquired.value}`);
  console.log(`From: ${request.user_alias_created.display_name}`);
});

webhookServer.getHandler().onCard((card) => {
  if (card.status === 'BLOCKED') {
    console.log(`Card blocked: ${card.name_on_card}`);
    alertSecurityTeam(card);
  }
});

// Start server and register with bunq
await webhookServer.start();
await webhookServer.register();

// Manual webhook management
const webhook = new BunqWebhook(bunq, account);

// Create webhook for specific URL
const webhookId = await webhook.create(account, 'https://myapp.com/bunq-webhook');

// List all webhooks
const webhooks = await webhook.list(account);

// Delete webhook
await webhook.delete(account, webhookId);

File Attachments

const attachment = new BunqAttachment(bunq);

// Upload a file
const attachmentUuid = await attachment.uploadFile(
  '/path/to/invoice.pdf',
  'Invoice #12345'
);

// Attach to payment
const payment = await BunqPayment.builder(bunq, account)
  .amount('150.00', 'EUR')
  .toIban('NL91ABNA0417164300', 'Accountant')
  .description('Services rendered')
  .attachments([attachmentUuid])
  .create();

// Upload from buffer
const buffer = await generateReport();
const uuid = await attachment.uploadBuffer(
  buffer,
  'report.pdf',
  'application/pdf',
  'Monthly Report'
);

// Get attachment content
const content = await attachment.getContent(attachmentUuid);
await fs.writeFile('downloaded.pdf', content);

Export Statements

// Export last month as PDF
await new ExportBuilder(bunq, account)
  .asPdf()
  .lastMonth()
  .downloadTo('/path/to/statement.pdf');

// Export date range as CSV
await new ExportBuilder(bunq, account)
  .asCsv()
  .dateRange('2024-01-01', '2024-03-31')
  .regionalFormat('EUROPEAN')
  .downloadTo('/path/to/transactions.csv');

// Export as MT940 for accounting software
await new ExportBuilder(bunq, account)
  .asMt940()
  .lastQuarter()
  .downloadTo('/path/to/statement.sta');

// Stream export for large files
const exportStream = await new ExportBuilder(bunq, account)
  .asCsv()
  .lastYear()
  .stream();

exportStream.pipe(fs.createWriteStream('large-export.csv'));

User & Session Management

// Get user information
const user = await bunq.getUser();
console.log(`Logged in as: ${user.displayName}`);
console.log(`User type: ${user.type}`); // UserPerson, UserCompany, etc.

// Update user settings
await user.update({
  dailyLimitWithoutConfirmationLogin: '100.00',
  notificationFilters: [
    { category: 'PAYMENT', notificationDeliveryMethod: 'PUSH' }
  ]
});

// Session management
const session = bunq.apiContext.getSession();
console.log(`Session expires: ${session.expiryTime}`);

// Manual session refresh
await bunq.apiContext.refreshSession();

// Save session for later use
const sessionData = bunq.apiContext.exportSession();
await fs.writeFile('bunq-session.json', JSON.stringify(sessionData));

// Restore session
const savedSession = JSON.parse(await fs.readFile('bunq-session.json'));
bunq.apiContext.importSession(savedSession);

Advanced Usage

OAuth Integration

// Create OAuth client
const oauth = new BunqOAuth({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  redirectUri: 'https://yourapp.com/callback'
});

// Generate authorization URL
const authUrl = oauth.getAuthorizationUrl({
  state: 'random-state-string',
  accounts: ['NL91ABNA0417164300']  // Pre-select accounts
});

// Exchange code for access token
const token = await oauth.exchangeCode(authorizationCode);

// Use OAuth token with bunq client
const bunq = new BunqAccount({
  accessToken: token.access_token,
  environment: 'PRODUCTION'
});

Error Handling

import { BunqApiError, BunqRateLimitError, BunqAuthError } from '@apiclient.xyz/bunq';

try {
  await payment.create();
} catch (error) {
  if (error instanceof BunqApiError) {
    // Handle API errors
    console.error('API Error:', error.errors);
    error.errors.forEach(e => {
      console.error(`- ${e.error_description}`);
    });
  } else if (error instanceof BunqRateLimitError) {
    // Handle rate limiting
    console.error('Rate limited. Retry after:', error.retryAfter);
    await sleep(error.retryAfter * 1000);
  } else if (error instanceof BunqAuthError) {
    // Handle authentication errors
    console.error('Authentication failed:', error.message);
    await bunq.reinitialize();
  } else {
    // Handle other errors
    console.error('Unexpected error:', error);
  }
}

Pagination

// Paginate through all transactions
async function* getAllTransactions(account: BunqMonetaryAccount) {
  let olderId: number | false = false;
  
  while (true) {
    const transactions = await account.getTransactions({
      count: 200,
      older_id: olderId
    });
    
    if (transactions.length === 0) break;
    
    yield* transactions;
    olderId = transactions[transactions.length - 1].id;
  }
}

// Usage
for await (const transaction of getAllTransactions(account)) {
  console.log(`${transaction.created}: ${transaction.description}`);
}

Sandbox Testing

// Create sandbox environment
const sandboxBunq = new BunqAccount({
  apiKey: '',  // Will be generated
  deviceName: 'My Test App',
  environment: 'SANDBOX'
});

// Create sandbox user with €1000 balance
const apiKey = await sandboxBunq.createSandboxUser();
console.log('Sandbox API key:', apiKey);

// Re-initialize with the generated key
const bunq = new BunqAccount({
  apiKey: apiKey,
  deviceName: 'My Test App',
  environment: 'SANDBOX'
});
await bunq.init();

// Sandbox-specific features
await sandboxBunq.topUpSandboxAccount(account.id, '500.00');
await sandboxBunq.simulateCardTransaction(card.id, '25.00', 'NL');

Security Best Practices

  1. API Key Storage: Never commit API keys to version control

    const bunq = new BunqAccount({
      apiKey: process.env.BUNQ_API_KEY,
      deviceName: 'Production App',
      environment: 'PRODUCTION'
    });
    
  2. IP Whitelisting: Restrict API access to specific IPs

    const bunq = new BunqAccount({
      apiKey: process.env.BUNQ_API_KEY,
      permittedIps: ['1.2.3.4', '5.6.7.8']
    });
    
  3. Webhook Verification: Always verify webhook signatures

    app.post('/webhook', (req, res) => {
      const signature = req.headers['x-bunq-client-signature'];
      const isValid = bunq.verifyWebhookSignature(req.body, signature);
    
      if (!isValid) {
        return res.status(401).send('Invalid signature');
      }
    
      // Process webhook...
    });
    

Migration Guide

From @bunq-community/bunq-js-client

// Old
import BunqJSClient from '@bunq-community/bunq-js-client';
const bunqJSClient = new BunqJSClient();

// New
import { BunqAccount } from '@apiclient.xyz/bunq';
const bunq = new BunqAccount({
  apiKey: 'your-api-key',
  deviceName: 'My App'
});

// Old
await bunqJSClient.install();
await bunqJSClient.registerDevice();
await bunqJSClient.registerSession();

// New - all handled in one call
await bunq.init();

Testing

The library includes comprehensive test coverage:

# Run all tests
npm test

# Run specific test suites
npm run test:basic      # Core functionality
npm run test:payments   # Payment features
npm run test:webhooks   # Webhook functionality
npm run test:session    # Session management
npm run test:errors     # Error handling
npm run test:advanced   # Advanced features

Requirements

  • Node.js 14.x or higher
  • TypeScript 4.5 or higher (for TypeScript users)

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Support

License

MIT licensed | © Lossless GmbH


For further information read the linked docs at the top of this readme.

By using this npm module you agree to our privacy policy

repo-footer

Description
an unofficial TypeScript api client for the bunq API
Readme 2.7 MiB
Languages
TypeScript 100%