Skip to main content

What are transactions?

Transactions represent the movement of funds between balances in your ledger. Every transaction records a financial event, moving money from a source balance to a destination balance. Blnk ensures that all transactions follow double-entry accounting principles, maintaining data integrity and providing a complete audit trail. Transactions in Blnk:
  • Move funds between balances atomically
  • Track both source and destination
  • Support multiple states (pending, committed, voided)
  • Handle multi-destination splits
  • Enable inflight (pending) operations
  • Maintain precision with big integer arithmetic

Transaction structure

The Transaction model in Blnk contains the following fields:
transaction_id
string
Unique identifier for the transaction (auto-generated)
amount
float64
Transaction amount in major units (e.g., 100.00 for $100)
precise_amount
big.Int
Transaction amount in minor units for precision
precision
float64
Multiplier for converting amount to minor units (100 for 2 decimals)
rate
float64
Exchange rate for currency conversion
source
string
Source balance ID (where funds come from)
destination
string
Destination balance ID (where funds go to)
sources
Distribution[]
Multiple source balances with distribution rules
destinations
Distribution[]
Multiple destination balances with distribution rules
reference
string
required
External reference or transaction ID from your system
currency
string
Currency code for the transaction
description
string
Human-readable description of the transaction
status
string
Transaction status: “QUEUED”, “APPLIED”, “INFLIGHT”, “VOID”, “REJECTED”
hash
string
Cryptographic hash of transaction data for integrity
parent_transaction
string
ID of parent transaction for splits and refunds
allow_overdraft
boolean
Whether to allow negative balances
overdraft_limit
float64
Maximum allowed negative balance
inflight
boolean
Whether this is a pending/inflight transaction
atomic
boolean
Whether to ensure atomic execution in batch operations
skip_queue
boolean
Process immediately without queuing
created_at
timestamp
When the transaction was created
effective_date
timestamp
When the transaction takes effect (for backdating)
scheduled_for
timestamp
When to execute the transaction (for scheduling)
inflight_expiry_date
timestamp
When inflight transaction expires and auto-voids
meta_data
object
Custom metadata for additional information

Example transaction object

{
  "transaction_id": "txn_abc123def456",
  "amount": 100.00,
  "precise_amount": "10000",
  "precision": 100,
  "source": "bal_source123",
  "destination": "bal_dest456",
  "reference": "payment_001",
  "currency": "USD",
  "description": "Payment for order #1234",
  "status": "APPLIED",
  "hash": "a1b2c3d4e5f6...",
  "allow_overdraft": false,
  "inflight": false,
  "created_at": "2024-03-04T10:30:00Z",
  "meta_data": {
    "order_id": "order_1234",
    "payment_method": "card"
  }
}

Creating a transaction

Create a simple transaction between two balances:
curl -X POST http://localhost:5001/transactions \
  -H "Content-Type: application/json" \
  -d '{
    "source": "bal_source123",
    "destination": "bal_dest456",
    "amount": 100,
    "precision": 100,
    "currency": "USD",
    "reference": "payment_001",
    "description": "Payment for services"
  }'
{
  "transaction_id": "txn_abc123def456",
  "amount": 100,
  "precise_amount": "10000",
  "precision": 100,
  "source": "bal_source123",
  "destination": "bal_dest456",
  "reference": "payment_001",
  "currency": "USD",
  "description": "Payment for services",
  "status": "APPLIED",
  "hash": "a1b2c3d4e5f6...",
  "created_at": "2024-03-04T10:30:00Z"
}
The reference field must be unique for each transaction. Blnk uses it for idempotency - if you retry the same request with the same reference, you’ll get the existing transaction instead of creating a duplicate.

Transaction lifecycle and states

Transactions move through different states during their lifecycle:

QUEUED

The transaction is scheduled for processing. This happens when:
  • Transaction is scheduled for a future time
  • System is processing at high volume
  • Transaction is part of a batch operation

APPLIED

The transaction has been successfully committed. Balances have been updated and the transaction is permanent.

INFLIGHT

The transaction is pending. Funds are held but not yet committed. The transaction can be:
  • Committed: Convert to APPLIED status
  • Voided: Reverse the hold and cancel the transaction
See Inflight transactions below.

VOID

The transaction has been cancelled or reversed. For inflight transactions, this releases the hold. For applied transactions, this creates a reversal transaction.

REJECTED

The transaction failed validation or processing. Common reasons:
  • Insufficient balance (when allow_overdraft is false)
  • Invalid source or destination balance
  • Validation errors

Sources, destinations, and distributions

Single source and destination

The simplest transaction moves money from one balance to another:
{
  "source": "bal_source123",
  "destination": "bal_dest456",
  "amount": 100,
  "precision": 100
}

Multiple destinations (splits)

Split funds from one source to multiple destinations:
{
  "source": "bal_customer_wallet",
  "destinations": [
    {
      "identifier": "bal_merchant",
      "distribution": "90%"
    },
    {
      "identifier": "bal_platform_fee",
      "distribution": "10%"
    }
  ],
  "amount": 100,
  "precision": 100
}

Distribution types

Blnk supports three distribution types:

Percentage distribution

Use percentages to split amounts:
{
  "identifier": "bal_merchant",
  "distribution": "85%"
}

Fixed amount distribution

Specify exact amounts:
{
  "identifier": "bal_platform_fee",
  "distribution": "5.00"
}
Or use precise amounts in minor units:
{
  "identifier": "bal_platform_fee",
  "precise_distribution": "500"
}

Left distribution

Allocate remaining funds after other distributions:
{
  "identifier": "bal_merchant",
  "distribution": "left"
}

Example: Payment with fees

Split a $100 payment between merchant (95%) and platform fee (5%):
curl -X POST http://localhost:5001/transactions \
  -H "Content-Type: application/json" \
  -d '{
    "source": "bal_customer_wallet",
    "destinations": [
      {
        "identifier": "bal_merchant",
        "distribution": "95%"
      },
      {
        "identifier": "bal_platform_fee",
        "distribution": "5%"
      }
    ],
    "amount": 100,
    "precision": 100,
    "currency": "USD",
    "reference": "payment_with_fee_001"
  }'
This creates:
  • Parent transaction: Links all related transactions
  • Child transaction 1: $95.00 to merchant
  • Child transaction 2: $5.00 to platform fee

Inflight transactions

Inflight transactions hold funds without immediately committing them. This is useful for:
  • Payment authorizations
  • Escrow operations
  • Two-phase commits
  • Reversible operations

Creating an inflight transaction

curl -X POST http://localhost:5001/transactions \
  -H "Content-Type: application/json" \
  -d '{
    "source": "bal_customer",
    "destination": "bal_merchant",
    "amount": 100,
    "precision": 100,
    "currency": "USD",
    "reference": "auth_001",
    "inflight": true,
    "inflight_expiry_date": "2024-03-05T00:00:00Z"
  }'
The funds are held in the source balance’s inflight_debit_balance and destination’s inflight_credit_balance.

Committing an inflight transaction

To apply the held funds:
curl -X POST http://localhost:5001/transactions/txn_abc123def456/commit
This moves funds from inflight to actual balances and changes status to APPLIED.

Voiding an inflight transaction

To cancel and release the hold:
curl -X POST http://localhost:5001/transactions/txn_abc123def456/void
This releases the held funds and changes status to VOID.
Inflight transactions automatically void when they reach their inflight_expiry_date. Set expiry dates to prevent funds from being held indefinitely.

Scheduled transactions

Schedule transactions for future execution:
curl -X POST http://localhost:5001/transactions \
  -H "Content-Type: application/json" \
  -d '{
    "source": "bal_source",
    "destination": "bal_dest",
    "amount": 100,
    "precision": 100,
    "currency": "USD",
    "reference": "scheduled_payment_001",
    "scheduled_for": "2024-03-10T09:00:00Z"
  }'
The transaction will be processed at the specified time.

Refunds and reversals

To refund a transaction, create a new transaction in the opposite direction:
curl -X POST http://localhost:5001/transactions \
  -H "Content-Type: application/json" \
  -d '{
    "source": "bal_merchant",
    "destination": "bal_customer",
    "amount": 100,
    "precision": 100,
    "currency": "USD",
    "reference": "refund_payment_001",
    "description": "Refund for payment_001",
    "parent_transaction": "txn_original123",
    "meta_data": {
      "refund_reason": "customer_request"
    }
  }'
The parent_transaction field links the refund to the original transaction.

Bulk transactions

Process multiple transactions in a single request:
curl -X POST http://localhost:5001/transactions/bulk \
  -H "Content-Type: application/json" \
  -d '{
    "transactions": [
      {
        "source": "bal_source1",
        "destination": "bal_dest1",
        "amount": 50,
        "precision": 100,
        "currency": "USD",
        "reference": "bulk_001_1"
      },
      {
        "source": "bal_source2",
        "destination": "bal_dest2",
        "amount": 75,
        "precision": 100,
        "currency": "USD",
        "reference": "bulk_001_2"
      }
    ],
    "atomic": true
  }'
With atomic: true, all transactions succeed together or all fail together.

Managing transactions

Retrieve a transaction

curl http://localhost:5001/transactions/txn_abc123def456

List transactions

# Get recent transactions
curl "http://localhost:5001/transactions?limit=20&offset=0"

# Filter by balance
curl "http://localhost:5001/transactions?source_eq=bal_source123"

# Filter by status
curl "http://localhost:5001/transactions?status_eq=APPLIED"

# Filter by date range
curl "http://localhost:5001/transactions?created_at_gte=2024-03-01&created_at_lte=2024-03-31"

Query transactions for a balance

curl "http://localhost:5001/balances/bal_abc123/transactions?limit=50"

Best practices

Always provide unique reference values. This enables:
  • Idempotency (safe retries)
  • Easy correlation with your system
  • Better debugging and support
Good reference patterns:
  • payment_${orderId}
  • transfer_${timestamp}_${userId}
  • refund_${originalReference}
Use meta_data to store relevant transaction context:
{
  "meta_data": {
    "order_id": "order_1234",
    "payment_method": "card_ending_4242",
    "customer_email": "[email protected]",
    "ip_address": "192.0.2.1"
  }
}
Set allow_overdraft: false (default) to prevent negative balances. When a transaction fails due to insufficient funds, handle it in your application:
{
  "error": "insufficient balance"
}
If you need to allow overdrafts, set limits:
{
  "allow_overdraft": true,
  "overdraft_limit": 10000
}
When you need to verify something before committing funds:
  1. Create inflight transaction (hold funds)
  2. Perform verification (check with payment provider, etc.)
  3. Commit or void based on result
Always set inflight_expiry_date to prevent indefinite holds.
Ensure the precision value matches your currency:
  • Standard currencies (USD, EUR): 100 (2 decimals)
  • Zero-decimal currencies (JPY, KRW): 1
  • Cryptocurrencies: May vary (Bitcoin: 100000000)
Inconsistent precision can lead to calculation errors.

Next steps

Balances

Learn about the balances that transactions move funds between

Double-Entry Accounting

Understand the accounting principles behind Blnk

Bulk Transactions

Process thousands of transactions efficiently

Reconciliation

Match transactions with external records

Build docs developers (and LLMs) love