Transactions

Void inflight transactions

Release one hold, many holds, or every leg in a split or batch.

After you create an inflight transaction, void it when the transfer should not settle. Void releases whatever is still held and leaves settled balances unchanged. Blnk creates a child transaction with VOID status.

Void takes no amount. If you committed part of the hold first, void releases only the leftover. It does not reverse amount already applied.

Once voided, the hold cannot be committed again.

Scenarios when inflight transactions are finished processing

Void one transaction

Void the transaction

Call Update inflight with the inflight transaction id and "status": "void".

PUT /transactions/inflight/{transaction_id}
curl -X PUT "http://YOUR_BLNK_INSTANCE_URL/transactions/inflight/{transaction_id}" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "void"
  }'
const response = await blnk.Transactions.updateStatus(
  '{transaction_id}',
  {
    status: 'void',
  },
);
transaction, _, _ := client.Transaction.Update(
    "{transaction_id}",
    blnkgo.UpdateStatus{
        Status: blnkgo.InflightStatusVoid,
    },
)
response = blnk.transactions.update_status(
  "{transaction_id}",
  {
    "status": "void",
  },
)
ApiResponse<JsonNode> response = blnk.transactions().updateStatus(
    "{transaction_id}",
    UpdateTransactionStatus.create()
        .status("void"));
{
  "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
  "status": "INFLIGHT",
  "queued": true
}

queued: true means the void is waiting on a worker, not that the hold is already released. With skip_queue: true, the same call returns the VOID child and sets parent_transaction to the inflight id.

Continuing from the earlier example, a full void of the $100 hold leaves balances like this:

balance_idMain balanceInflight balanceInflight creditInflight debit
balance_A200000
balance_B0000

After a partial commit of $40, voiding the remaining $60 clears the leftover inflight amounts and keeps the $40 already applied:

balance_idMain balanceInflight balanceInflight creditInflight debit
balance_A160000
balance_B40000

Void the transaction

Open the inflight transaction. Void cancels it and releases what is still held.

Commit and void on a transaction

Select Void transaction, or press V, then confirm.

Void transaction confirmation

A new linked transaction is created with status VOID.

Void multiple inflights

Void the holds

Use Bulk void inflight for holds you created one by one. Send transaction_ids, not the transactions array used by bulk commit. Each request accepts at most 100 ids.

POST /transactions/inflight/bulk/void
curl -X POST "http://YOUR_BLNK_INSTANCE_URL/transactions/inflight/bulk/void" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_ids": [
      "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
      "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30"
    ]
  }'
const response = await blnk.Transactions.bulkVoidInflight({
  transaction_ids: [
    'txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94',
    'txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30',
  ],
});
result, _, _ := client.Transaction.BulkVoidInflight(blnkgo.BulkVoidInflightRequest{
    TransactionIDs: []string{
        "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
        "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30",
    },
})
response = blnk.transactions.bulk_void_inflight({
  "transaction_ids": [
    "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
    "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30",
  ],
})
ApiResponse<JsonNode> response = blnk.transactions().bulkVoidInflight(
    BulkVoidInflightRequest.create()
        .transactionIds(List.of(
            "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
            "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30"
        )));

Read the result

One failed item does not block the rest.

StatusCodeMeaning
queuedQUEUEDNew void job added.
queuedALREADY_QUEUEDAlready queued. The duplicate is skipped.
{
  "succeeded": 2,
  "failed": 0,
  "results": [
    {
      "transaction_id": "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
      "status": "queued",
      "code": "QUEUED"
    },
    {
      "transaction_id": "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30",
      "status": "queued",
      "code": "QUEUED"
    }
  ]
}

QUEUED and ALREADY_QUEUED both count toward succeeded. Set skip_queue: true to process each item in the request. Those results use succeeded or failed instead of queued.

Void a split or a bulk batch

Choose the parent id

A split uses the root transaction_id. A bulk batch uses batch_id. That id voids every pending leg.

Void the split or batch

Call Update inflight with that id and "status": "void". Do not send that id to Bulk void inflight.

PUT /transactions/inflight/{parent_id}
curl -X PUT "http://YOUR_BLNK_INSTANCE_URL/transactions/inflight/{parent_id}" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "void"
  }'
const response = await blnk.Transactions.updateStatus(
  '{parent_id}',
  {
    status: 'void',
  },
);
transaction, _, _ := client.Transaction.Update(
    "{parent_id}",
    blnkgo.UpdateStatus{
        Status: blnkgo.InflightStatusVoid,
    },
)
response = blnk.transactions.update_status(
  "{parent_id}",
  {
    "status": "void",
  },
)
ApiResponse<JsonNode> response = blnk.transactions().updateStatus(
    "{parent_id}",
    UpdateTransactionStatus.create()
        .status("void"));
{
  "transaction_id": "txn_0b59f6e6-6c4a-4efa-915c-526f77ef61ab",
  "status": "INFLIGHT",
  "queued": true
}

On the default queue, the response stays INFLIGHT with queued: true. Every leg and its later VOID child shares meta_data.QUEUED_PARENT_TRANSACTION.

With skip_queue: true, the response reflects the first leg processed. Each leg’s VOID child points at that leg through parent_transaction, so check each leg on its own.

Verify the outcome

Blnk sends transaction.void once a void is finalized.

Match the child the same way as a commit: meta_data.QUEUED_PARENT_TRANSACTION on the default queue, or parent_transaction when the void skipped the queue. The child status is VOID.

The event payload and the filter are on Verify the outcome.

Schedule inflight expiry

A hold stays inflight until you commit it, void it, or it expires.

Set inflight_expiry_date when you create the transaction to cap how long funds can remain held.

If the transaction is still INFLIGHT when that time passes, Blnk voids it. The outcome matches a manual void: a child with VOID status, and the held amount released without changing settled balances.

curl -X POST "http://YOUR_BLNK_INSTANCE_URL/transactions" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "precise_amount": 10000,
    "precision": 100,
    "reference": "ref_001adcfgf",
    "currency": "USD",
    "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
    "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
    "description": "For vacation",
    "inflight": true,
    "inflight_expiry_date": "2024-12-21T01:36:46+01:00"
  }'
const response = await blnk.Transactions.create({
  precise_amount: 10000,
  precision: 100,
  reference: 'ref_001adcfgf',
  currency: 'USD',
  source: 'bln_28edb3e5-c168-4127-a1c4-16274e7a28d3',
  destination: 'bln_ebcd230f-6265-4d4a-a4ca-45974c47f746',
  description: 'For vacation',
  inflight: true,
  inflight_expiry_date: '2024-12-21T01:36:46+01:00',
});
transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
    ParentTransaction: blnkgo.ParentTransaction{
        PreciseAmount: big.NewInt(10000),
        Precision: 100,
        Reference: "ref_001adcfgf",
        Currency: "USD",
        Source: "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
        Destination: "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
        Description: "For vacation",
    },
    Inflight: true,
    InflightExpiryDate: "2024-12-21T01:36:46+01:00",
})
response = blnk.transactions.create({
  "precise_amount": 10000,
  "precision": 100,
  "reference": "ref_001adcfgf",
  "currency": "USD",
  "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3",
  "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746",
  "description": "For vacation",
  "inflight": True,
  "inflight_expiry_date": "2024-12-21T01:36:46+01:00",
})
ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .preciseAmount(10000)
        .precision(100)
        .reference("ref_001adcfgf")
        .currency("USD")
        .source("bln_28edb3e5-c168-4127-a1c4-16274e7a28d3")
        .destination("bln_ebcd230f-6265-4d4a-a4ca-45974c47f746")
        .description("For vacation")
        .inflight(true)
        .inflightExpiryDate("2024-12-21T01:36:46+01:00"));
{
  "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d",
  "status": "INFLIGHT",
  "inflight": true,
  "inflight_expiry_date": "2024-12-21T01:36:46+01:00"
}

Error handling

Update inflight returns 400, 404, and 409 when a void fails validation or conflicts with the current state.

On the default queue, Blnk rejects a second commit or void before enqueueing another job. With skip_queue: true, Blnk validates in the request and returns the error immediately.

CodeWhen it happens
GEN_CONFLICTA commit or void is already queued for this inflight transaction.
TXN_ALREADY_COMMITTEDYou try to void a fully committed inflight transaction.
TXN_ALREADY_VOIDEDThe inflight transaction is already voided.
TXN_NOT_INFLIGHTThe transaction is not INFLIGHT.
TXN_INVALID_STATUS_ACTIONstatus is not commit or void.
TXN_NOT_FOUNDThe transaction id does not exist, or it is not the split or batch parent.
{
  "error": "a commit or void is already queued for this transaction",
  "error_detail": {
    "code": "GEN_CONFLICT",
    "message": "a commit or void is already queued for this transaction"
  }
}

To resolve the error:

CodeWhat to do
GEN_CONFLICTWait for the queued job to finish, or verify the outcome before sending another request.
TXN_ALREADY_COMMITTEDTreat the hold as finished. To reverse it, refund the applied transaction.
TXN_ALREADY_VOIDEDTreat the hold as cancelled. Do not void it again.
TXN_NOT_INFLIGHTUse the original inflight id, not an APPLIED or VOID child.
TXN_INVALID_STATUS_ACTIONSet status to void.
TXN_NOT_FOUNDFor a split or batch, use the parent id from create, not a leg or child id.

Bulk void inflight returns 400 before any item is processed when the body is invalid. Sending transactions instead of transaction_ids returns TXN_BULK_EMPTY, because unrecognized fields are dropped.

Per-item failures return 200 with failed entries in results. One failed item does not block the batch.

CodeWhen it happens
TXN_BULK_EMPTYtransaction_ids is missing, empty, or the wrong shape.
TXN_BULK_LIMIT_EXCEEDEDMore than 100 ids in one request.

With skip_queue: true, a failed item can return NOT_FOUND, ALREADY_COMMITTED, ALREADY_VOIDED, NOT_INFLIGHT, LOCKED, or INTERNAL_ERROR. See API error codes.