@blnkfinance/blnk-typescript

中文文档
1.6.0发布于 2026/10/03MITTypeScriptCommonJS近 30 天下载 4576

Blnk Finance SDK in TypeScript

原始使用文档
文档目录75 节
  1. Blnk TypeScript SDK Documentation
  2. 1. Installation
  3. Prerequisites
  4. Step 1: Clone the Blnk Repository
  5. Step 2: Install Blnk TypeScript SDK
  6. Step 3: Setting Up Configuration
  7. 2. Launching Blnk
  8. Health check
  9. 3. Using the Blnk CLI
  10. 4. Creating Your First Ledger
  11. What is a Ledger?
  12. Step-by-Step: Creating a Ledger
  13. Using Blnk Cloud proxy
  14. Updating a ledger name
  15. List ledgers, balances, and transactions
  16. 5. Creating Identities
  17. Tokenize identity fields
  18. Delete identity
  19. 6. Creating Balances
  20. Step-by-Step: Creating a Balance
  21. Get balance
  22. Get balance by indicator
  23. Update balance identity
  24. Create balance snapshots
  25. Get historical balance
  26. Get balance lineage
  27. Delete balance monitor
  28. 6. Recording Transactions
  29. Step-by-Step: Recording a Transaction
  30. Dry-run a transaction
  31. Atomic split transactions
  32. Get transaction by ID
  33. Get transaction by reference
  34. Recover queued transactions
  35. Get transaction lineage
  36. Update inflight transaction status
  37. Bulk commit inflight transactions
  38. Bulk void inflight transactions
  39. Refund a transaction
  40. Create transaction response
  41. 7. Bulk Transactions
  42. Overview
  43. Method Signature
  44. Parameters
  45. Usage Examples
  46. Bulk transaction response
  47. Response Format
  48. Validation Rules
  49. Error Handling
  50. Migration from Single Transactions
  51. 8. Viewing Ledgers, Balances, and Transactions
  52. Search
  53. Typesense search
  54. DB filter
  55. Typesense reindex
  56. Reconciliation
  57. Update a matching rule
  58. Delete a matching rule
  59. Get reconciliation status
  60. Start batch reconciliation
  61. Instant reconciliation
  62. Metadata
  63. Update metadata
  64. Hooks
  65. Register a hook
  66. List hooks
  67. View a hook
  68. Update a hook
  69. Delete a hook
  70. API Keys
  71. Create an API key
  72. List API keys
  73. Revoke an API key
  74. Additional Resources
  75. Issue Reporting

Blnk logo

Blnk TypeScript SDK Documentation


1. Installation

Prerequisites

Ensure that you have the following installed on your machine:

  • Docker and Docker Compose for running Blnk’s server locally.
  • Node.js (v14 or later) and npm for installing the Blnk TypeScript SDK.

Step 1: Clone the Blnk Repository

To start, clone the Blnk repository from GitHub:

git clone https://github.com/blnkfinance/blnk && cd blnk

Step 2: Install Blnk TypeScript SDK

Install the Blnk TypeScript SDK in your project:

npm install @blnkfinance/[email protected] --save

v1.6.0 targets Blnk Core 0.15.4. Cloud proxy instanceId is available in 1.6.0. See RELEASE.md for the error catalogue, Search.multiSearch, and list methods (Ledgers.list, LedgerBalances.list, Transactions.list, BalanceMonitor.listByBalanceId).

Step 3: Setting Up Configuration

In your cloned directory, create a configuration file named blnk.json with the following content:

{
  "project_name": "Blnk",
  "data_source": {
    "dns": "postgres://postgres:password@postgres:5432/blnk?sslmode=disable"
  },
  "redis": {
    "dns": "redis:6379"
  },
  "server": {
    "domain": "blnk.io",
    "ssl": false,
    "ssl_email": "[email protected]",
    "port": "5001"
  },
  "notification": {
    "slack": {
      "webhook_url": "https://hooks.slack.com"
    }
  }
}

This configuration sets up connections to PostgreSQL and Redis, specifies your server details, and allows Slack notifications if needed.


2. Launching Blnk

With Docker Compose, launch the Blnk server:

docker compose up

Once running, your server will be accessible at http://localhost:5001.

Health check

System.health checks whether Blnk Core is running (GET /health):

const health = await blnk.System.health();
// health.status === 200
// health.data?.status === 'UP'

3. Using the Blnk CLI

The Blnk CLI offers quick access to manage ledgers, balances, and transactions. To verify the installation and view available commands, use:

blnk --help

4. Creating Your First Ledger

What is a Ledger?

In Blnk, ledgers are used to categorize balances for organized tracking. When you first install Blnk, an internal ledger called the General Ledger is created by default.

Step-by-Step: Creating a Ledger

Using the SDK, create a ledger for user accounts:

import { BlnkInit } from '@blnkfinance/blnk-typescript';

const blnk = await BlnkInit('<secret_key_if_set>', { baseUrl: 'http://localhost:5001' });
const { Ledgers } = blnk;

const newLedger = await Ledgers.create({
    name: "Customer Savings Account",
    meta_data: {
        project_owner: "YOUR_APP_NAME"
    }
});
console.log("Ledger Created:", newLedger);

This creates a new ledger for storing customer balances.

Using Blnk Cloud proxy

To call Core through Cloud Proxy, pass your Cloud API key (or OAuth access token) and the Core instance_id. The SDK keeps the same Core request bodies and paths, prefixes them with /proxy, and sends instance_id as a query parameter.

import { BlnkInit, DEFAULT_CLOUD_API_BASE_URL } from '@blnkfinance/blnk-typescript';

const blnk = BlnkInit('<cloud_api_key>', {
  baseUrl: DEFAULT_CLOUD_API_BASE_URL, // https://api.cloud.blnkfinance.com
  instanceId: 'instance_...',
});

const newLedger = await blnk.Ledgers.create({
  name: 'Customer Savings Account',
});
// POST https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=instance_...

instanceId is available in 1.6.0 and is required for proxy routing. Omit it when you talk to Core directly (baseUrl is your Core origin).

Updating a ledger name

Rename an existing ledger without changing its ID or affecting balances and transactions:

const updatedLedger = await Ledgers.update(
  'ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc',
  { name: 'Updated Customer Savings Account' },
);
console.log('Ledger Updated:', updatedLedger);

List ledgers, balances, and transactions

Ledgers.list, LedgerBalances.list, and Transactions.list wrap Core's GET /ledgers, GET /balances, and GET /transactions (present since ~0.14.x; this SDK is aligned with Core 0.15.4). Omit options to use Core's default page (limit=10 / offset=0 for ledgers and balances, limit=20 for transactions). Invalid limit or offset is rejected client-side with HTTP 400.

const firstPage = await Ledgers.list();
const nextPage = await Ledgers.list({ limit: 10, offset: 10 });

const balances = await LedgerBalances.list({ limit: 50 });

const recentTransactions = await Transactions.list({ limit: 100 });

5. Creating Identities

Register customers or organizations before linking them to balances. Only identity_type is required; all other fields are optional and match the Create Identity API reference. The SDK validates identity_type and field formats (for example identity_id, dob, gender) when you provide them.

Minimal create:

const { Identity } = blnk;

const minimal = await Identity.create({
  identity_type: 'individual',
});

Full example with optional caller-supplied identity_id (idt_ + UUID) and ISO 8601 dob:

const { Identity } = blnk;

const newIdentity = await Identity.create({
  identity_id: 'idt_11111111-1111-4111-8111-111111111111',
  identity_type: 'individual',
  first_name: 'Jane',
  last_name: 'Doe',
  gender: 'female',
  dob: '1990-01-15T00:00:00Z',
  email_address: '[email protected]',
  phone_number: '+1234567890',
  nationality: 'US',
  category: 'customer',
  street: '123 Main St',
  country: 'USA',
  state: 'NY',
  post_code: '10001',
  city: 'New York',
});
console.log('Identity Created:', newIdentity);

Tokenize identity fields

MethodEndpointUse case
Identity.getTokenizedFields(id)GET /identities/{identity_id}/tokenized-fieldsList fields currently tokenized on an identity
Identity.tokenizeField(id, field)POST /identities/{identity_id}/tokenize/{field}Tokenize one PII field on an identity
Identity.tokenize(id, data)POST /identities/{identity_id}/tokenizeTokenize multiple PII fields on an identity
Identity.detokenize(id, data)POST /identities/{identity_id}/detokenizeDetokenize fields and return original values
Identity.detokenizeField(id, field)GET /identities/{identity_id}/detokenize/{field}Detokenize one field and return its original value
const { Identity } = blnk;

const tokenizedFields = await Identity.getTokenizedFields(identity.data!.identity_id);
// tokenizedFields.data?.tokenized_fields — e.g. ["FirstName", "EmailAddress"]

// Tokenize a single field (PascalCase struct name in the path).
const oneField = await Identity.tokenizeField(identity.data!.identity_id, 'EmailAddress');
// oneField.data?.message — "Field tokenized successfully"

// Use PascalCase struct field names — not the snake_case JSON keys on IdentityData.
const tokenized = await Identity.tokenize(identity.data!.identity_id, {
  fields: ['FirstName', 'LastName', 'EmailAddress', 'PhoneNumber'],
});
// tokenized.data?.message — "Fields tokenized successfully"

// Detokenize specific fields, or pass { fields: [] } to detokenize all tokenized fields.
const restored = await Identity.detokenize(identity.data!.identity_id, {
  fields: ['FirstName', 'EmailAddress'],
});
// restored.data?.fields — e.g. { FirstName: "Jane", EmailAddress: "[email protected]" }

// Detokenize a single field and read the original value.
const email = await Identity.detokenizeField(identity.data!.identity_id, 'EmailAddress');
// email.data?.value — e.g. "[email protected]"

Field names must be Core struct names (FirstName, EmailAddress, …). Passing first_name or email_address from IdentityData will be rejected.

See the Get tokenized fields reference, Tokenize field reference, Tokenize identity reference, Detokenize field reference, and Detokenize identity reference.

Delete identity

MethodEndpointUse case
Identity.delete(id)DELETE /identities/{identity_id}Remove an identity record (Core 0.15.0+)
const { Identity } = blnk;

const deleted = await Identity.delete(identity.data!.identity_id);
// deleted.data?.message — "Identity deleted successfully"

See the Delete identity reference.


6. Creating Balances

Balances represent the store of value within a ledger, like a wallet or account. Each balance belongs to a ledger.

Step-by-Step: Creating a Balance

To create a balance, specify the ledger_id and other details:

const { LedgerBalances } = blnk;

const newBalance = await LedgerBalances.create({
    ledger_id: "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc",
    currency: "USD",
    meta_data: {
        first_name: "Alice",
        last_name: "Hart",
        account_number: "1234567890"
    }
});
console.log("Balance Created:", newBalance);

Create a General Ledger balance on Core 0.15.3+ with ledger_id: "general_ledger_id" and an @ indicator:

const glBalance = await LedgerBalances.create({
  ledger_id: "general_ledger_id",
  currency: "USD",
  indicator: "@WorldUSD",
});

With fund lineage tracking enabled (requires identity_id):

const lineageBalance = await LedgerBalances.create({
  ledger_id: "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc",
  identity_id: "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
  currency: "USD",
  track_fund_lineage: true,
  allocation_strategy: "FIFO", // FIFO | LIFO | PROPORTIONAL
});

Get balance

LedgerBalances.get retrieves a balance by ID (GET /balances/{balance_id}). Pass { from_source: true } to reconstruct the balance from transactions instead of snapshots:

const response = await LedgerBalances.get(
  'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f',
  { from_source: true },
);

// response.data.balance_id
// response.data.balance

Get balance by indicator

LedgerBalances.getByIndicator retrieves a balance by its indicator and currency (GET /balances/indicator/{indicator}/currency/{currency}):

const response = await LedgerBalances.getByIndicator('@World', 'USD');

// response.data.balance_id
// response.data.indicator
// response.data.currency

Update balance identity

LedgerBalances.updateIdentity links a balance to an identity (PUT /balances/{id}/identity):

const response = await LedgerBalances.updateIdentity(
  'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f',
  { identity_id: 'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6' },
);

// response.data.message

Create balance snapshots

LedgerBalances.createSnapshot triggers daily balance snapshots in batches (POST /balances-snapshots). Omit batch_size or pass zero to use the server default (1000):

const response = await LedgerBalances.createSnapshot({ batch_size: 500 });

// response.data.message

Get historical balance

LedgerBalances.getAt retrieves a balance at a specific timestamp (GET /balances/{balance_id}/at):

const response = await LedgerBalances.getAt(
  'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f',
  { timestamp: '2025-02-24T08:55:26Z', from_source: true },
);

// response.data.balance.balance_id
// response.data.balance.balance
// response.data.timestamp

Get balance lineage

LedgerBalances.getLineage retrieves the provider breakdown for a balance with fund lineage enabled (GET /balances/{balance_id}/lineage):

const response = await LedgerBalances.getLineage(
  'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f',
);

// response.data.balance_id
// response.data.total_with_lineage
// response.data.providers

Delete balance monitor

MethodEndpointUse case
BalanceMonitor.list()GET /balance-monitorsList all balance monitors
BalanceMonitor.listByBalanceId(id)GET /balance-monitors/balances/{balance_id}List monitors for one balance
BalanceMonitor.delete(id)DELETE /balance-monitors/{monitor_id}Remove a balance monitor (Core 0.15.0+)
const { BalanceMonitor } = blnk;

const forBalance = await BalanceMonitor.listByBalanceId(
  'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f',
);
// forBalance.data — MonitorDataResp[]

const deleted = await BalanceMonitor.delete(monitor.data!.monitor_id);
// deleted.data?.message — "BalanceMonitor deleted successfully"

See the Delete balance monitor reference.


6. Recording Transactions

Transactions track financial activities within your application. Blnk ensures that each transaction is both immutable and idempotent.

Step-by-Step: Recording a Transaction

To record a transaction, you’ll need the source and destination balance IDs:

const { Transactions } = blnk;

const newTransaction = await Transactions.create({
    amount: 750,
    reference: "ref_001adcfgf",
    currency: "USD",
    precision: 100,
    source: "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
    destination: "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
    description: "Sent from app",
    meta_data: {
        sender_name: "John Doe",
        sender_account: "00000000000"
    }
});
console.log("Transaction Recorded:", newTransaction);

Dry-run a transaction

On Core 0.15.3+, set dry_run: true on create, bulk create, refund, inflight updateStatus, bulkCommitInflight, or bulkVoidInflight to preview balances without writing anything. The response is HTTP 200 with would_apply and balances — not a posted transaction.

const preview = await Transactions.create({
  amount: 120,
  precision: 100,
  reference: 'ref_card_settle_4821',
  description: 'Card settlement preview',
  currency: 'USD',
  source: '@WorldUSD',
  destination: '@MyBalance',
  dry_run: true,
});
// preview.status === 200
// preview.data?.dry_run === true
// preview.data?.would_apply

dry_run is discriminated so a preview is never typed as a posted transaction. Requests typed with a plain request type keep the posted response, so existing code is unaffected:

const body: CreateTransactions<Meta> = {/* ... */};
const posted = await Transactions.create(body);
posted.data?.transaction_id; // still typed

// Preview: pass the flag inline, or type the body as DryRun<...>
const previewBody: DryRun<CreateTransactions<Meta>> = {...body, dry_run: true};
const preview = await Transactions.create(previewBody);
preview.data?.would_apply; // TransactionPreview — no transaction_id

When the flag is only known at runtime the response widens to the posted-or-preview union, so narrow before reading posted-only fields:

const result = await Transactions.create({...body, dry_run: shouldPreview});

if (result.data && 'transaction_id' in result.data) {
  console.log('posted', result.data.transaction_id);
} else if (result.data) {
  console.log('preview', result.data.would_apply);
}

See Dry-run transactions.

Atomic split transactions

Set atomic: true when creating a split transaction (destinations or sources) so all legs succeed or fail together:

const response = await Transactions.create({
  amount: 1000,
  precision: 100,
  reference: 'atomic_split_ref_001',
  description: 'Atomic fee split',
  currency: 'USD',
  source: '@FundingPool',
  destinations: [
    { identifier: 'bln_fee', distribution: '240.23' },
    { identifier: 'bln_recipient', distribution: 'left' },
  ],
  atomic: true,
  skip_queue: true,
});

Get transaction by ID

Transactions.get retrieves a transaction by its transaction_id (GET /transactions/{transaction_id}):

const response = await Transactions.get('txn_04551509-d7d3-4eab-a1fd-2eb12809b5a4');

Get transaction by reference

Transactions.getByReference retrieves a transaction by its reference (GET /transactions/reference/{reference}):

const response = await Transactions.getByReference('ref_04551509-d7d3-4eab-a1fd-2eb12809b5a4');

Recover queued transactions

Transactions.recoverQueue manually triggers recovery of stuck queued transactions (POST /transactions/recover). Optionally pass a threshold duration query (e.g. 5m, 1h):

const response = await Transactions.recoverQueue({ threshold: '5m' });

// response.data.recovered, response.data.threshold

Get transaction lineage

Transactions.getLineage retrieves fund allocation and shadow transactions for a transaction (GET /transactions/{transaction_id}/lineage):

const response = await Transactions.getLineage('txn_8d2ce2f0-0d75-4a91-9d43-2ad2c2e6b9ad');

// response.data.transaction_id
// response.data.fund_allocation
// response.data.shadow_transactions

Update inflight transaction status

Core 0.15.0 queues inflight commit and void by default. The response includes queued: true (status is typically QUEUED until the worker processes the action). Send skip_queue: true for immediate APPLIED or VOID responses (previous synchronous behavior).

Transactions.updateStatus accepts precise_amount for partial commits on inflight transactions (in addition to amount). Omit both fields to commit the full remaining inflight amount:

// Queued commit (default in Core 0.15.0)
const queued = await Transactions.updateStatus(transactionId, { status: 'commit' });
// queued.data?.queued === true

// Synchronous commit
await Transactions.updateStatus(transactionId, {
  status: 'commit',
  skip_queue: true,
});

// Partial commit in minor units
await Transactions.updateStatus(transactionId, {
  status: 'commit',
  precise_amount: 50000,
  skip_queue: true,
});

Bulk commit inflight transactions

Transactions.bulkCommitInflight commits multiple independently-created inflight transactions in one request (POST /transactions/inflight/bulk/commit). By default, each item is queued (status: 'queued' in results). Pass skip_queue: true for synchronous processing. Omit amount and precise_amount on an item to commit the full remaining inflight amount:

const response = await Transactions.bulkCommitInflight({
  skip_queue: true,
  transactions: [
    { transaction_id: 'txn_11111111-1111-4111-8111-111111111111' },
    { transaction_id: 'txn_22222222-2222-4222-8222-222222222222', amount: 40 },
    {
      transaction_id: 'txn_33333333-3333-4333-8333-333333333333',
      precise_amount: 125034,
    },
  ],
});

// response.data.succeeded, response.data.failed, response.data.results

const commitPreview = await Transactions.bulkCommitInflight({
  dry_run: true,
  transactions: [
    { transaction_id: 'txn_11111111-1111-4111-8111-111111111111' },
  ],
});
// commitPreview.data?.dry_run === true
// commitPreview.data?.would_apply

Bulk void inflight transactions

Transactions.bulkVoidInflight voids multiple independently-created inflight transactions in one request (POST /transactions/inflight/bulk/void). By default, each item is queued. Pass skip_queue: true for synchronous void:

const response = await Transactions.bulkVoidInflight({
  skip_queue: true,
  transaction_ids: [
    'txn_11111111-1111-4111-8111-111111111111',
    'txn_22222222-2222-4222-8222-222222222222',
  ],
});

// response.data.succeeded, response.data.failed, response.data.results

const voidPreview = await Transactions.bulkVoidInflight({
  dry_run: true,
  transaction_ids: ['txn_11111111-1111-4111-8111-111111111111'],
});

Refund a transaction

Transactions.refund accepts an optional body with skip_queue to process the refund synchronously. On Core 0.15.3+ you can also set description, meta_data, and dry_run. Omit the body to queue the refund (default):

// Queued refund (default)
await Transactions.refund(transactionId);

// Synchronous refund
await Transactions.refund(transactionId, { skip_queue: true });

// Refund with description and metadata
await Transactions.refund(transactionId, {
  description: 'Card reversal',
  meta_data: { type: 'refund' },
});

// Preview without writing
await Transactions.refund(transactionId, { dry_run: true });

Create transaction response

Transactions.create resolves to a CreateTransactionResponse that matches the Core API reference, including hash, parent_transaction, allow_overdraft, and inflight date fields (scheduled_for, inflight_expiry_date, inflight_commit_date):

interface CreateTransactionResponse<T extends Record<string, unknown>> {
  transaction_id: string;
  amount: number;
  precision: number;
  precise_amount: number | string;
  reference: string;
  description: string;
  rate?: number;
  currency: string;
  status: StatusType;
  hash: string;
  parent_transaction: string; // empty string when none
  allow_overdraft: boolean;
  inflight: boolean;
  created_at: Date | string;
  scheduled_for: Date | string;
  inflight_expiry_date: Date | string;
  inflight_commit_date: Date | string;
  effective_date?: Date | string;
  source?: string;
  destination?: string;
  meta_data?: T;
}

7. Bulk Transactions

The Blnk JavaScript SDK supports bulk transactions, allowing you to submit multiple transaction records in a single request for improved performance and atomic transaction processing.

Overview

The bulk transactions API provides the following benefits:

  • Performance: Submit multiple transactions in one API call
  • Atomicity: Ensure all transactions succeed or fail together (when atomic: true)
  • Async Processing: Process large batches asynchronously
  • Inflight Support: Create multiple inflight transactions that can be committed or voided later

Method Signature

async createBulk<T extends Record<string, unknown>>(data: BulkTransactions<T>)

Parameters

BulkTransactions Interface

interface BulkTransactions<T extends Record<string, unknown>> {
  atomic?: boolean;        // Optional: All transactions succeed or fail together
  inflight?: boolean;      // Optional: Create transactions as inflight
  run_async?: boolean;     // Optional: Process transactions asynchronously
  skip_queue?: boolean;    // Optional: Process without queuing
  transactions: CreateTransactions<T>[]; // Required: Array of transaction objects
}

Usage Examples

Basic Bulk Transactions

const { Transactions } = blnk;

// Basic bulk transactions without additional options
const basicBulkData = {
  transactions: [
    {
      amount: 1000,
      precision: 100,
      reference: 'bulk_txn_001',
      description: 'Payment 1',
      currency: 'USD',
      source: '@source_account_1',
      destination: '@destination_account_1',
    },
    {
      amount: 2000,
      precision: 100,
      reference: 'bulk_txn_002',
      description: 'Payment 2',
      currency: 'USD',
      source: '@source_account_2',
      destination: '@destination_account_2',
    },
  ],
};

const response = await Transactions.createBulk(basicBulkData);
console.log('Bulk transaction response:', response);

Atomic Bulk Transactions

// Atomic transactions - all succeed or all fail
const atomicBulkData = {
  atomic: true,
  transactions: [
    {
      amount: 5000,
      precision: 100,
      reference: 'atomic_txn_001',
      description: 'Atomic payment 1',
      currency: 'USD',
      source: '@source_account_1',
      destination: '@destination_account_1',
    },
    {
      amount: 3000,
      precision: 100,
      reference: 'atomic_txn_002',
      description: 'Atomic payment 2',
      currency: 'USD',
      source: '@source_account_2',
      destination: '@destination_account_2',
    },
  ],
};

const atomicResponse = await Transactions.createBulk(atomicBulkData);

Async Bulk Transactions with All Options

// Process transactions asynchronously with atomic and inflight options
const asyncBulkData = {
  atomic: true,
  inflight: true,
  run_async: true,
  transactions: [
    {
      amount: 12000,
      precision: 100,
      reference: 'async_txn_001',
      description: 'Async atomic inflight payment 1',
      currency: 'USD',
      source: '@source_account_1',
      destination: '@destination_account_1',
      allow_overdraft: true,
      inflight_expiry_date: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48 hours
      meta_data: {
        department: 'sales',
        project: 'Q4_campaign',
      },
    },
    {
      amount: 8500,
      precision: 100,
      reference: 'async_txn_002',
      description: 'Async atomic inflight payment 2',
      currency: 'USD',
      source: '@source_account_2',
      destination: '@destination_account_2',
      allow_overdraft: true,
      inflight_expiry_date: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48 hours
      meta_data: {
        department: 'marketing',
        project: 'Q4_campaign',
      },
    },
  ],
};

const asyncResponse = await Transactions.createBulk(asyncBulkData);

Bulk transaction response

Transactions.createBulk resolves to a BulkTransactionResponse that matches the Core API reference (batch_id, status, transaction_count, and optional message for async batches):

interface BulkTransactionResponse {
  batch_id: string;
  status: 'applied' | 'inflight' | 'queued' | string;
  transaction_count?: number; // present on synchronous success
  message?: string;           // present when run_async is true
}

Response Format

The bulk API returns batch metadata (not nested transaction objects). See Bulk transaction response above for the typed shape.

Validation Rules

The bulk transactions API validates the following:

  1. Required Fields: transactions array must be provided and cannot be empty
  2. Max Size: transactions array cannot exceed 10,000 items
  3. Transaction Validation: Each transaction in the array must pass standard transaction validation
  4. Unique References: All transaction references must be unique within the bulk request
  5. Boolean Flags: atomic, inflight, run_async, and skip_queue must be booleans if provided
  6. Standard Transaction Rules: All existing transaction validation rules apply to each transaction

Error Handling

Compare response.error?.code against the constants in BlnkErrorCode, which mirror the full Core 0.15.4 catalogue (TXN_ALREADY_REFUNDED, BAL_NOT_FOUND, TXN_INSUFFICIENT_FUNDS, TXN_DUPLICATE_REFERENCE, LGR_NOT_FOUND, and so on):

import {BlnkErrorCode} from '@blnkfinance/blnk-typescript';

const refund = await Transactions.refund(transactionId);
if (refund.error?.code === BlnkErrorCode.TXN_ALREADY_REFUNDED) {
  // 409: already refunded, or this id is itself a refund — nothing to do
}
try {
  const response = await Transactions.createBulk(bulkData);
  if (response.status === 201) {
    console.log('Bulk transactions created successfully:', response.data);
  } else {
    console.error('Bulk transaction error:', response.message);
  }
} catch (error) {
  console.error('Network or system error:', error);
}

Migration from Single Transactions

Converting from single transactions to bulk transactions is straightforward:

Before (Single Transactions)

const tx1 = await Transactions.create(transactionData1);
const tx2 = await Transactions.create(transactionData2);

After (Bulk Transactions)

const bulkResponse = await Transactions.createBulk({
  transactions: [transactionData1, transactionData2]
});

This migration provides better performance and the option for atomic processing.


8. Viewing Ledgers, Balances, and Transactions

The Blnk CLI allows you to list all ledgers, balances, and transactions quickly:

  • List Ledgers: blnk ledgers list
  • List Balances: blnk balances list
  • List Transactions: blnk transactions list

Blnk search APIs on the Search service:

MethodEndpointUse case
Search.search(params, collection)POST /search/{collection}Full-text search via Typesense
Search.multiSearch(params)POST /multi-searchSeveral Typesense searches in one request (Core 0.10.0+)
Search.filter(params, collection)POST /{collection}/filterStructured DB filters (Core 0.13.2+)
Search.startReindex(options?)POST /search/reindexRebuild Typesense index from DB
Search.getReindexStatus()GET /search/reindexPoll reindex progress

Collections: ledgers, balances, transactions, identities.

const { Search } = blnk;

const results = await Search.search(
  { q: 'payment', per_page: 10 },
  'transactions',
);

Search several collections in one request (POST /multi-search; results come back in the same order as the searches):

const { Search } = blnk;

const multi = await Search.multiSearch({
  searches: [
    { collection: 'transactions', q: 'ref_001', query_by: 'reference' },
    { collection: 'balances', q: '*', filter_by: 'currency:USD' },
    { collection: 'ledgers', q: 'savings', query_by: 'name' },
  ],
});

const transactionHits = multi.data?.results[0]?.hits;

Requires Blnk Core v0.10.0+ (router.POST("/multi-search", a.MultiSearch) in api/api.go). This SDK is aligned with Core 0.15.4.

DB filter

const { Search } = blnk;

const filtered = await Search.filter(
  {
    filters: [{ field: 'status', operator: 'eq', value: 'APPLIED' }],
    logical_operator: 'and',
    sort_by: 'created_at',
    sort_order: 'desc',
    include_count: true,
    limit: 20,
    offset: 0,
  },
  'transactions',
);
// filtered.data?.data — matching transaction records
// filtered.data?.total_count — present when include_count is true

See the Search via DB reference for supported operators and fields.

Typesense reindex

const { Search } = blnk;

const reindex = await Search.startReindex({ batch_size: 1000 });
// reindex.data?.message — "Reindex operation started"
// reindex.data?.progress.status — "pending" | "in_progress" | "completed" | "failed"

See the Start reindex reference.

Poll progress after starting a reindex:

const status = await Search.getReindexStatus();
// status.data?.status — "in_progress" | "completed" | "failed"
// status.data?.phase — e.g. "indexing_transactions" or "done"

See the Get reindex status reference.


Reconciliation

MethodEndpointUse case
Reconciliation.upload(file, source)POST /reconciliation/uploadUpload external data file
Reconciliation.createMatchingRule(data)POST /reconciliation/matching-rulesDefine match criteria
Reconciliation.updateMatchingRule(id, data)PUT /reconciliation/matching-rules/{rule_id}Update an existing matching rule
Reconciliation.deleteMatchingRule(id)DELETE /reconciliation/matching-rules/{rule_id}Remove a matching rule
Reconciliation.run(data)POST /reconciliation/startStart batch reconciliation from upload
Reconciliation.runInstant(data)POST /reconciliation/start-instantReconcile inline external transactions
Reconciliation.get(id)GET /reconciliation/{reconciliation_id}View reconciliation status and counts

Update a matching rule

const { Reconciliation } = blnk;

const updated = await Reconciliation.updateMatchingRule('rule_abc123', {
  name: 'Updated matcher',
  description: 'Amount with 2% drift matcher',
  criteria: [
    { field: 'amount', operator: 'equals', allowable_drift: 0.02 },
    { field: 'currency', operator: 'equals' },
  ],
});
// updated.data?.rule_id, updated.data?.updated_at

See the Update matching rule reference.

Delete a matching rule

const { Reconciliation } = blnk;

const deleted = await Reconciliation.deleteMatchingRule('rule_abc123');
// deleted.data?.message — "Matching rule deleted successfully"

See the Delete matching rule reference.

Get reconciliation status

const { Reconciliation } = blnk;

const status = await Reconciliation.get('recon_3803ea0d-28b4-4c73-a36b-5a9eb7a3edfd');
// status.data?.status — e.g. started, in_progress, completed, failed
// status.data?.matched_transactions, unmatched_transactions

See the View reconciliation details reference.

Start batch reconciliation

Reconciliation.run starts reconciliation from a prior upload (POST /reconciliation/start). Core 0.15.0 returns only a reconciliation ID — poll Reconciliation.get(id) or listen for reconciliation.completed / reconciliation.failed webhooks for results.

const { Reconciliation } = blnk;

const started = await Reconciliation.run({
  upload_id: upload.data!.upload_id,
  strategy: 'one_to_one',
  dry_run: true,
  grouping_criteria: 'amount',
  matching_rule_ids: [rule.data!.rule_id],
});
// started.data?.reconciliation_id — use with Reconciliation.get() or webhooks

See the Start reconciliation reference.

Instant reconciliation

const { Reconciliation } = blnk;

const instant = await Reconciliation.runInstant({
  external_transactions: [
    {
      id: 'txn_1',
      amount: 5.49,
      reference: 'INV-2023-002',
      currency: 'GBP',
      description: 'Card payment',
      date: '2024-11-15T14:25:30Z',
      source: 'bank-api',
    },
  ],
  strategy: 'one_to_one',
  dry_run: true,
  matching_rule_ids: ['rule_abc123'],
});
// instant.data?.reconciliation_id — reconciliation run ID

See the Instant reconciliation reference.


Metadata

MethodEndpointUse case
Metadata.update(id, data)POST /{id}/metadataAdd or update metadata on a ledger, transaction, balance, or identity

Update metadata

const { Metadata } = blnk;

const updated = await Metadata.update('ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc', {
  meta_data: {
    project_owner: 'Acme LLC',
    update_status: 'Approved',
  },
});
// updated.data?.meta_data — merged metadata from Core

See the Update metadata reference.


Hooks

MethodEndpointUse case
Hooks.create(data)POST /hooksRegister a pre- or post-transaction webhook
Hooks.list(options?)GET /hooksList hooks, optionally by type query
Hooks.get(id)GET /hooks/{id}View hook details
Hooks.update(id, data)PUT /hooks/{id}Update an existing webhook
Hooks.delete(id)DELETE /hooks/{id}Delete a webhook

Hook management requires the master key (server.secret_key) in X-Blnk-Key. Regular API keys return 403.

Register a hook

const { Hooks } = blnk;

const hook = await Hooks.create({
  name: 'Pre-transaction validation',
  url: 'https://api.example.com/validate',
  type: 'PRE_TRANSACTION',
  active: true,
  timeout: 30,
  retry_count: 3,
});
// hook.data?.id — registered hook ID

See the Register hooks reference.

List hooks

const allHooks = await Hooks.list();
const preTxnHooks = await Hooks.list({ type: 'PRE_TRANSACTION' });

See the List hooks by type reference.

View a hook

const hookDetails = await Hooks.get(hook.data!.id);
// hookDetails.data?.name, hookDetails.data?.active, etc.

See the View hooks reference.

Update a hook

const updated = await Hooks.update(hook.data!.id, {
  name: 'Pre-transaction validation (updated)',
  url: 'https://api.example.com/validate-v2',
  type: 'PRE_TRANSACTION',
  active: false,
  timeout: 45,
  retry_count: 5,
});

See the Update hooks reference.

Delete a hook

const deleted = await Hooks.delete(hook.data!.id);
// deleted.data?.message — "hook deleted successfully"

See the Delete hooks reference.


API Keys

MethodEndpointUse case
ApiKeys.create(data)POST /api-keysCreate a scoped API key
ApiKeys.list(options?)GET /api-keysList API keys for an owner
ApiKeys.delete(id, options?)DELETE /api-keys/{id}Revoke an API key

API key management requires the master key or scoped permissions (api-keys:write to create, api-keys:read to list, api-keys:delete to revoke). The raw key value is only returned once at creation.

Create an API key

const { ApiKeys } = blnk;

const apiKey = await ApiKeys.create({
  name: 'Service Account',
  owner: 'merchant_a',
  scopes: ['ledgers:read', 'balances:write'],
  expires_at: '2026-03-11T00:00:00Z',
});
// apiKey.data?.key — store securely; shown only once

See the Create API key reference.

List API keys

const keys = await ApiKeys.list({ owner: 'merchant_a' });
// keys.data?.[0]?.api_key_id, keys.data?.[0]?.scopes, etc.

See the List API keys reference.

Revoke an API key

await ApiKeys.delete('api_key_abc123', { owner: 'merchant_a' });
// 204 No Content on success; response.data is null

See the Revoke API key reference.


Additional Resources

For more examples and advanced use cases, please refer to the Examples Code.

Issue Reporting

If you encounter any issues, please report them on GitHub.

版本与发布记录

@blnkfinance/blnk-typescript 共发布 7 个版本,首次发布于 2024/10/28;过去 12 个月发布了 4 个版本。

版本发布日期距今
1.6.0latest2026/10/031 个月前
1.5.02026/09/241 个月前
1.4.02026/09/032 个月前
1.3.02026/07/113 个月前
1.0.22024/11/122 年前
1.0.12024/10/282 年前

关于 @blnkfinance/blnk-typescript 的常见问题

@blnkfinance/blnk-typescript 是什么?

Blnk Finance SDK in TypeScript

如何安装 @blnkfinance/blnk-typescript?

在项目目录运行 npm install @blnkfinance/blnk-typescript,或使用 pnpm add / yarn add / bun add。

@blnkfinance/blnk-typescript 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@blnkfinance/blnk-typescript 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@blnkfinance/blnk-typescript 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@blnkfinance/blnk-typescript 可以商用吗?

@blnkfinance/blnk-typescript 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@blnkfinance/blnk-typescript 的下载量有多少?

根据 npm 官方下载统计,@blnkfinance/blnk-typescript 在 2026/10/09 – 2026/11/07 这 30 天内被下载 4,576 次,日均约 153 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@blnkfinance/blnk-typescript 还在维护吗?

最新版本 1.6.0 发布于 2026/10/03(1 个月前);至今共发布 7 个版本,其中过去 12 个月发布了 4 个;首次发布于 2024/10/28。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。