Overview

Transactions are used to enable money movement between two or more balances. These can be payments, transfers, settlements, internal treasury management, etc. All transactions in Blnk are recorded with the double entry principle, i.e., there is a source and a corresponding destination for each transaction entry.

In this guide, you’ll learn:

  1. Transaction properties
  2. Recording a transaction
  3. How transactions work in Blnk
  4. Managing insufficient funds

Transaction properties

  1. Immutability: Once recorded, transactions in Blnk cannot be modified or deleted. This fundamental property ensures the integrity and reliability of your transaction history, preventing any unauthorized alterations. See also: Transaction hashing

  2. Idempotency: Each transaction in Blnk produces the same result whether executed once or multiple times. This is crucial for maintaining data consistency, especially during network failures or system retries.

    Blnk implements idempotency by requiring a unique reference for every transaction. This reference serves as a transaction identifier, preventing duplicate processing and ensuring consistent outcomes.


Recording a transaction

To record a transaction, call the record-transaction endpoint:

POST http://YOUR_BLNK_INSTANCE_URL/transactions

With the following request body:

If this is your first transaction, the participating balances will start at 0. To ensure the transaction is successful, enable overdrafts as shown above, allowing the source balance to go negative.

Learn more about Overdrafts and Negative Balances.

Response
{
    "id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
    "source": "@FundingPool",
    "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
    "reference": "ref_001adcfgf",
    "amount": 750,
    "precision": 100,
    "precise_amount": 75000,
    "currency": "USD",
    "description": "Fund with starting balance amount",
    "status": "QUEUED",
    "created_at": "2024-12-21T01:36:46.997063436Z",
    "meta_data": {
      "sender_name": "John Doe",
      "sender_account": "00000000000"
    }
}
FieldDescriptionRequiredType
amountThe transaction amount.Yesfloat
referenceYour unique reference to ensure idempotency.Yesstring
currencyShort code for your asset class. See also: Asset classesYesstring
precisionPrecision for the currency/asset passed. See also: PrecisionNoint64
sourceSender’s balance IDYesstring
destinationRecipient’s balance ID.Yesstring
descriptionDescription or narration of the transaction.Nostring
meta_dataCustom data associated with the transactionNoobject

Passing detailed data with the meta_data object is encouraged; it provides you with 360-degree insights about each transaction record. Examples of data you can pass include sender_name, account_number, bank_name, receiver_name, payment_id, ip_address, location, payment_method, etc.

Next, you can view a list of all transactions in your ledger:

bash
blnk transactions list

How transactions work in Blnk

Every transaction goes through multiple states in Blnk, and each state change is recorded as a separate record in the database. This gives you complete traceability, allowing to see the lifecycle of a transaction from initiation to completion.

Each state is connected to the previous state through a parent_transaction attribute. Here’s how it works:

1

Transaction lifecycle

By default, every transaction begins in a QUEUED state.

You can choose to skip this QUEUED state altogether and directly process your transactions. Learn more: Skip Transaction Queue

Within milliseconds of being queued, Blnk processes it and transitions it to one of three possible states:

  • INFLIGHT: If the transaction request includes "inflight": "true".
    • APPLIED: If the inflight transaction is committed.
    • VOID: If the inflight transaction is voided.
  • APPLIED: If inflight is not enabled.
  • REJECTED: If the source has insufficient funds and "allow_overdraft" is not enabled.

Here’s an example response when you record a new transaction on Blnk:

Record transaction response
{
  "precise_amount": 10000000,
  "amount": 100000,
  "rate": 0,
  "precision": 100,
  "transaction_id": "txn_d4951810-706c-44d1-be45-6f254be0e167",
  "parent_transaction": "",
  "source": "bln_7769aedf-bc88-49f7-bbb2-118f121daee6",
  "destination": "bln_c42e4bbd-4ea9-494a-89be-7634b0c1e41a",
  "reference": "ref_e55c4f33-bff7-4c30-9b9f-5d2d10a29b7a",
  "currency": "USD",
  "description": "Sample transaction",
  "status": "QUEUED",
  "hash": "3ef19e9cd9aba07d33068d096da1e1596f1270a70c9aa2d160206b4d6da553dc",
  "allow_overdraft": true,
  "inflight": false,
  "created_at": "2025-02-02T02:35:16.606614793Z",
  "scheduled_for": "0001-01-01T00:00:00Z",
  "inflight_expiry_date": "0001-01-01T00:00:00Z"
}
2

Record creation and tracking

When a transaction moves to its next state (INFLIGHT, APPLIED, or REJECTED), Blnk creates a new record that maintains a connection to the original queued transaction. This connection is maintained in two ways:

  1. Parent-Child Relationship: The new record’s parent_transaction field stores the original transaction’s transaction_id. This creates a clear lineage between related transaction records.
  2. Reference Tracking: To ensure each record remains uniquely identifiable while preserving its relationship to other states, Blnk:
    • Uses the same base reference as the original transaction.
    • Appends a _q suffix to create a unique reference.

For example, if the original transaction has:

{
  ...
  "transaction_id": "txn_123",
  "reference": "payment_abc"
}

The next state’s record would have:

{
  ...
  "parent_transaction": "txn_123",
  "reference": "payment_abc_q"
}
3

Tracking transaction status

You can track the final state of your transactions using either of these methods:

  1. Reference Search: Query transactions using the original reference. Blnk will return the INFLIGHT or APPLIED record associated with that reference.
    {
      "q": "payment_abc",
      "query_by": "reference"
    }
    
  2. Parent Transaction Search: Look up transactions using the original transaction_id as a parent transaction identifier. This returns the same result as the reference search but uses the parent-child relationship instead of the reference string.
    {
      "q": "txn_123",
      "query_by": "parent_transaction"
    }
    
4

Discarded transactions

A discarded transaction is not recorded in the ledger. Blnk discards transactions for one reason:

  1. Duplicate reference: Your new transaction reference matches an existing reference in your ledger. Blnk requires unique reference values per transaction. Options are timestamps (e.g. UNIX timestamp), random string or UUID (e.g. ref_e55c4f33-bff7-4c30-9b9f-5d2d10a29b7a), or a business identifier like an order_id.
Make sure your request body match the Blnk Ledger API specifications. For example, avoid passing apply_overdraft instead of allow_overdraft.

Skip transaction queue

The skip_queue feature allows you to bypass the default transaction queuing system outlined above and process transactions directly. This is useful for scenarios where you need real-time transaction processing while still maintaining data consistency.

{
  "amount": 102.12,
  "precision": 100,
  ...
  "allow_overdraft": true,
  "skip_queue": true
}

How it works

When you enable skip_queue, the transaction:

  • Bypasses the normal queuing process
  • Executes immediately on the balance
  • Uses distributed locks via Redis to maintain consistency
  • Applies optimistic locking at the database level

When to use skip_queue

Use skip_queue for:

  • Real-time payment processing in your ledger
  • Interactive user sessions requiring immediate feedback
  • Time-sensitive financial operations

Avoid using for:

  • Complex transactions with multiple steps or dependencies
  • Transactions requiring external service validation
Consider rate limiting for high traffic scenarios. Learn how: blnk.json Configuration File

Managing insufficient funds

You can handle insufficient funds scenarios in two ways: through automatic rejection handling when transactions are attempted with insufficient balance, and through proactive balance checking before initiating transactions.

1. Automatic rejection

When a transaction is posted where the source balance has insufficient funds, Bink automatically handles this scenario to maintain ledger integrity. Blnk:

  1. Rejects the transaction.
  2. Records this rejection in your ledger using the same state tracking mechanism we discussed above (i.e. QUEUEDREJECTED).
  3. Sends a webhook notification to inform your system of the state change and new ledger record.
You can also preemptively track the transaction status (via reference or parent_transaction) to know if the transaction was successful or rejected.

2. Proactive balance verification

Instead of waiting for a transaction.rejected webhook, you can implement a preemptive balance check in your workflow. Here’s how:

First, query the balance of the sender using:

GET /balances/<source-balance-id>

This allows you to make an informed decision before initiating a transaction:

  • If the balance is sufficient, proceed with posting your transaction.
  • If the balance is insufficient, you can gracefully handle this scenario in your application by notifying the customer immediately, avoiding the need for webhook handling.

Insufficient funds for inflight transactions

For a detailed understanding of how insufficient funds can be managed in inflight scenarios, please refer to our Applying inflight guide.



Need help?

We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool.

To ask questions or discuss issues, please contact us or join our Discord community.

Get access to Blnk Cloud.

Manage your Blnk Ledger and explore advanced features (access control & collaboration, anomaly detection, secure storage & file management, etc.) in one dashboard.