Bulk transactions allow you to process hundreds or thousands of transactions in a single request. This is essential for payroll, mass payouts, dividend distributions, and batch refunds.
How bulk transactions work
When you submit a bulk transaction request:
Blnk validates all transactions upfront
Processes them in parallel using worker pools
Supports both synchronous and asynchronous modes
Provides atomic or non-atomic processing options
Returns a batch ID for tracking
Basic bulk transaction
Process multiple transactions in a single request:
Submit bulk transaction request
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 100.00 ,
"precision" : 100 ,
"reference" : "payroll_emp_001" ,
"currency" : "USD" ,
"source" : "bln_company_payroll" ,
"destination" : "bln_employee_001" ,
"description" : "Monthly salary"
},
{
"amount" : 150.00 ,
"precision" : 100 ,
"reference" : "payroll_emp_002" ,
"currency" : "USD" ,
"source" : "bln_company_payroll" ,
"destination" : "bln_employee_002" ,
"description" : "Monthly salary"
},
{
"amount" : 120.00 ,
"precision" : 100 ,
"reference" : "payroll_emp_003" ,
"currency" : "USD" ,
"source" : "bln_company_payroll" ,
"destination" : "bln_employee_003" ,
"description" : "Monthly salary"
}
]
}
Synchronous response
{
"batch_id" : "batch_abc123xyz" ,
"status" : "applied" ,
"transaction_count" : 3
}
All three transactions are processed immediately and the response confirms success.
Asynchronous processing
For large batches, process asynchronously to avoid timeouts:
Submit with async flag
POST /transactions/bulk
{
"transactions" : [
// ... array of transactions
],
"run_async" : true
}
Immediate response
{
"message" : "Bulk transaction processing started" ,
"batch_id" : "batch_xyz789abc" ,
"status" : "processing"
}
The request returns immediately with status processing.
Check status via webhook
Set up a webhook to receive completion notifications: {
"event" : "bulk_transaction.completed" ,
"data" : {
"batch_id" : "batch_xyz789abc" ,
"status" : "applied" ,
"transaction_count" : 1000 ,
"completed_at" : "2024-01-15T10:45:00Z"
}
}
Atomic bulk transactions
Ensure all transactions succeed or none are applied:
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 50.00 ,
"precision" : 100 ,
"reference" : "batch_refund_001" ,
"currency" : "USD" ,
"source" : "bln_merchant_wallet" ,
"destination" : "bln_customer_001"
},
{
"amount" : 75.00 ,
"precision" : 100 ,
"reference" : "batch_refund_002" ,
"currency" : "USD" ,
"source" : "bln_merchant_wallet" ,
"destination" : "bln_customer_002"
}
],
"atomic" : true
}
With atomic: true, if ANY transaction fails (e.g., insufficient balance), ALL transactions in the batch are rolled back.
Inflight bulk transactions
Create multiple authorization holds at once:
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 100.00 ,
"precision" : 100 ,
"reference" : "bulk_auth_001" ,
"currency" : "USD" ,
"source" : "bln_customer_wallet" ,
"destination" : "bln_merchant_wallet"
},
{
"amount" : 200.00 ,
"precision" : 100 ,
"reference" : "bulk_auth_002" ,
"currency" : "USD" ,
"source" : "bln_customer_wallet" ,
"destination" : "bln_merchant_wallet"
}
],
"inflight" : true
}
All transactions in the batch will have status: "INFLIGHT".
Skip queue option
Process transactions immediately without queueing:
POST /transactions/bulk
{
"transactions" : [
// ... transactions
],
"skip_queue" : true
}
Use skip_queue: true for high-priority transactions that need immediate processing. This bypasses the normal transaction queue.
Common use cases
Payroll processing
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 3500.00 ,
"precision" : 100 ,
"reference" : "payroll_jan_2024_emp_001" ,
"currency" : "USD" ,
"source" : "bln_company_payroll_account" ,
"destination" : "bln_employee_wallet_001" ,
"description" : "January 2024 salary" ,
"meta_data" : {
"employee_id" : "EMP001" ,
"month" : "2024-01" ,
"payment_type" : "salary"
}
},
{
"amount" : 4200.00 ,
"precision" : 100 ,
"reference" : "payroll_jan_2024_emp_002" ,
"currency" : "USD" ,
"source" : "bln_company_payroll_account" ,
"destination" : "bln_employee_wallet_002" ,
"description" : "January 2024 salary" ,
"meta_data" : {
"employee_id" : "EMP002" ,
"month" : "2024-01" ,
"payment_type" : "salary"
}
}
// ... more employees
],
"run_async" : true ,
"atomic" : true
}
Mass refunds
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 29.99 ,
"precision" : 100 ,
"reference" : "refund_order_12345" ,
"currency" : "USD" ,
"source" : "bln_merchant_account" ,
"destination" : "bln_customer_wallet_001" ,
"description" : "Refund for order #12345" ,
"meta_data" : {
"original_order_id" : "12345" ,
"refund_reason" : "product_recall"
}
},
{
"amount" : 49.99 ,
"precision" : 100 ,
"reference" : "refund_order_12346" ,
"currency" : "USD" ,
"source" : "bln_merchant_account" ,
"destination" : "bln_customer_wallet_002" ,
"description" : "Refund for order #12346" ,
"meta_data" : {
"original_order_id" : "12346" ,
"refund_reason" : "product_recall"
}
}
// ... more refunds
],
"run_async" : true
}
Dividend distribution
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 150.00 ,
"precision" : 100 ,
"reference" : "dividend_q4_2024_investor_001" ,
"currency" : "USD" ,
"source" : "bln_company_dividend_pool" ,
"destination" : "bln_investor_wallet_001" ,
"description" : "Q4 2024 dividend payment" ,
"meta_data" : {
"investor_id" : "INV001" ,
"shares" : 100 ,
"dividend_per_share" : 1.50 ,
"quarter" : "Q4-2024"
}
},
{
"amount" : 300.00 ,
"precision" : 100 ,
"reference" : "dividend_q4_2024_investor_002" ,
"currency" : "USD" ,
"source" : "bln_company_dividend_pool" ,
"destination" : "bln_investor_wallet_002" ,
"description" : "Q4 2024 dividend payment" ,
"meta_data" : {
"investor_id" : "INV002" ,
"shares" : 200 ,
"dividend_per_share" : 1.50 ,
"quarter" : "Q4-2024"
}
}
// ... more investors
],
"run_async" : true ,
"skip_queue" : false
}
Creator payouts
POST /transactions/bulk
{
"transactions" : [
{
"amount" : 500.00 ,
"precision" : 100 ,
"reference" : "payout_creator_jan_001" ,
"currency" : "USD" ,
"source" : "bln_platform_payout_account" ,
"destination" : "bln_creator_wallet_001" ,
"description" : "January earnings payout" ,
"meta_data" : {
"creator_id" : "creator_001" ,
"period" : "2024-01" ,
"total_views" : 50000 ,
"revenue_share" : 0.70
}
},
{
"amount" : 750.00 ,
"precision" : 100 ,
"reference" : "payout_creator_jan_002" ,
"currency" : "USD" ,
"source" : "bln_platform_payout_account" ,
"destination" : "bln_creator_wallet_002" ,
"description" : "January earnings payout" ,
"meta_data" : {
"creator_id" : "creator_002" ,
"period" : "2024-01" ,
"total_views" : 75000 ,
"revenue_share" : 0.70
}
}
// ... more creators
],
"run_async" : true
}
Batch size recommendations
Small batches (< 100) Use synchronous mode for immediate results
Medium batches (100-1000) Use async mode with run_async: true
Large batches (> 1000) Split into multiple async requests
Atomic batches Keep under 500 transactions for better performance
Worker pool configuration
Blnk processes bulk transactions using worker pools. The default configuration:
const (
asyncBulkSemaphore = semaphore . NewWeighted ( 100 ) // max 100 concurrent
asyncTxnSemaphore = semaphore . NewWeighted ( 20 ) // max 20 concurrent processors
)
From transaction.go:57-58
Error handling
Individual transaction failures (non-atomic)
With atomic: false, individual failures don’t stop the batch:
{
"batch_id" : "batch_123" ,
"status" : "applied" ,
"transaction_count" : 100 ,
"failed_count" : 3 ,
"errors" : [
{
"reference" : "payroll_emp_042" ,
"error" : "insufficient balance"
},
{
"reference" : "payroll_emp_055" ,
"error" : "balance not found"
},
{
"reference" : "payroll_emp_089" ,
"error" : "reference already used"
}
]
}
Atomic batch failure
With atomic: true, the entire batch fails if any transaction fails:
{
"error" : "transaction rejected" ,
"batch_id" : "batch_456" ,
"failed_transaction" : "payroll_emp_042" ,
"reason" : "insufficient balance"
}
All transactions in the batch are rolled back.
Implementation code (from api/transactions.go:456-493)
The bulk transaction handler:
func ( a Api ) CreateBulkTransactions ( c * gin . Context ) {
var req model . BulkTransactionRequest
if err := c . BindJSON ( & req ); err != nil {
c . JSON ( http . StatusBadRequest , gin . H { "error" : "Invalid request body: " + err . Error ()})
return
}
// Call the service layer to handle bulk transaction creation
result , err := a . blnk . CreateBulkTransactions ( c . Request . Context (), & req )
if err != nil {
logrus . WithError ( err ). WithField ( "batch_id" , result . BatchID ). Error ( "bulk transaction API error" )
c . JSON ( http . StatusBadRequest , gin . H {
"error" : result . Error ,
"batch_id" : result . BatchID ,
})
return
}
if req . RunAsync {
// Async request acknowledged
c . JSON ( http . StatusAccepted , gin . H {
"message" : "Bulk transaction processing started" ,
"batch_id" : result . BatchID ,
"status" : result . Status ,
})
} else {
// Synchronous request completed
c . JSON ( http . StatusCreated , gin . H {
"batch_id" : result . BatchID ,
"status" : result . Status ,
"transaction_count" : result . TransactionCount ,
})
}
}
Best practices
Unique references Ensure each transaction has a unique reference to prevent duplicates
Use async for scale Enable run_async for batches over 100 transactions
Implement webhooks Set up webhooks to track async batch completion
Monitor batch IDs Store batch IDs for tracking and reconciliation
Validate upfront Verify balances and data before submitting large batches
Handle failures gracefully Implement retry logic for failed transactions
Troubleshooting
Batch timeout
Problem : Large synchronous batches timing out
Solution : Use run_async: true or split into smaller batches
Insufficient balance for atomic batch
Problem : Atomic batch fails due to one insufficient balance
Solution : Pre-validate all balances before submitting:
for each transaction:
GET /balances/{balance_id}
verify balance > = transaction.amount
Duplicate references
Problem : Some transactions fail with “reference already used”
Solution : Generate unique references with timestamps or UUIDs:
const reference = `payroll_ ${ employeeId } _ ${ Date . now () } ` ;
Next steps
Webhooks Set up notifications for batch completion
Reconciliation Match bulk transactions with external records