> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blnkfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk Transactions

> Learn how to handle bulk transactions in Blnk

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

<Info>Available in version 0.9.0 and later.</Info>

The Bulk Transaction API enables you to process multiple transactions within a single request. It offers two processing options: **atomic processing**, where all transactions either succeed or fail as a unit, and **independent processing**, where each transaction is handled separately.

Additionally, the API supports asynchronous processing to efficiently manage large batches of transactions.

***

## Creating bulk transactions

<Steps>
  <Step title="Configure and submit the batch">
    Call the [Bulk Transactions API](/reference/bulk-transactions).

    | Field          | Description                                                                                                                                                                                        |
    | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `atomic`       | When `true`, either all transactions succeed or all fail. When `false`, transactions are processed independently.                                                                                  |
    | `inflight`     | When `true`, transactions are created in `INFLIGHT` status and require a separate commit. When `false`, transactions are processed immediately.                                                    |
    | `run_async`    | When `true`, processing happens in the background and results are delivered via webhook. When `false` or not provided, processing happens synchronously and results are returned in the response.  |
    | `skip_queue`   | Defaults to `false`. When `true`, transactions bypass the queue and are processed inline in the request. Affects duplicate-reference handling - see [Duplicate references](#duplicate-references). |
    | `transactions` | Array of transaction objects. Max of 10,000 transactions per request.                                                                                                                              |

    <CodeGroup>
      ```bash cURL wrap expandable theme={"system"}
      curl -X POST "http://localhost:5001/transactions/bulk" \
        -H "X-blnk-key: <api-key>" \
        -H "Content-Type: application/json" \
        -d '{
          "atomic": true,
          "inflight": false,
          "run_async": false,
          "skip_queue": false,
          "transactions": [
            {
              "precise_amount": 35890,
              "precision": 100,
              "reference": "unique_reference_1",
              "description": "Transaction description",
              "currency": "NGN",
              "source": "@source_account",
              "allow_overdraft": true,
              "destination": "@destination_account"
            },
            {
              "precise_amount": 35890,
              "precision": 100,
              "reference": "unique_reference_2",
              "description": "Transaction description",
              "currency": "NGN",
              "source": "@source_account",
              "allow_overdraft": true,
              "destination": "@destination_account"
            }
          ]
        }'
      ```

      ```typescript TypeScript wrap expandable theme={"system"}
      const response = await blnk.Transactions.createBulk({
        atomic: true,
        inflight: false,
        run_async: false,
        skip_queue: false,
        transactions: [
          {
            precise_amount: 35890,
            precision: 100,
            reference: 'unique_reference_1',
            description: 'Transaction description',
            currency: 'NGN',
            source: '@source_account',
            allow_overdraft: true,
            destination: '@destination_account',
          },
          {
            precise_amount: 35890,
            precision: 100,
            reference: 'unique_reference_2',
            description: 'Transaction description',
            currency: 'NGN',
            source: '@source_account',
            allow_overdraft: true,
            destination: '@destination_account',
          },
        ],
      });
      ```

      ```go Go wrap expandable theme={"system"}
      batch, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
        Atomic:    true,
        Inflight:  false,
        RunAsync:  false,
        SkipQueue: false,
        Transactions: []blnkgo.CreateTransactionRequest{
          {
            PreciseAmount:  35890,
            Precision:      100,
            Reference:      "unique_reference_1",
            Description:    "Transaction description",
            Currency:       "NGN",
            Source:         "@source_account",
            Destination:    "@destination_account",
            AllowOverdraft: true,
          },
          {
            PreciseAmount:  35890,
            Precision:      100,
            Reference:      "unique_reference_2",
            Description:    "Transaction description",
            Currency:       "NGN",
            Source:         "@source_account",
            Destination:    "@destination_account",
            AllowOverdraft: true,
          },
        },
      })
      ```
    </CodeGroup>
  </Step>

  <Step title="Confirm the response">
    Blnk returns a `batch_id` that identifies the batch. When `skip_queue: false`, Blnk stores `batch_id` as the queued parent transaction.

    Save it. You can use the same ID to retrieve, commit, refund, or void every transaction in the batch without tracking individual child IDs first. See [Retrieving transactions in a batch](#retrieving-transactions-in-a-batch).

    <Tabs>
      <Tab title="Synchronous">
        When `run_async` is `false`:

        ```json Response theme={"system"}
        {
          "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
          "status": "applied",
          "transaction_count": 2
        }
        ```

        <Note>
          On the default queued path, `status: "applied"` means the bulk request was **successfully submitted**, not that every child is already `APPLIED` on balances. Blnk enqueues each item for asynchronous processing.
        </Note>
      </Tab>

      <Tab title="Asynchronous">
        When `run_async` is `true`, Blnk accepts the batch and returns immediately with `status: "processing"`.

        Processing continues in the background. The HTTP response does not wait for each transaction to finish.

        Once done, Blnk sends one of the following webhook events with the final outcome.

        * `bulk_transaction.applied`
        * `bulk_transaction.inflight`
        * `bulk_transaction.failed`

        See [Webhook notifications](#webhook-notifications).

        ```json Response theme={"system"}
        {
          "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
          "status": "processing",
          "message": "Bulk transaction processing started"
        }
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

***

## Retrieving transactions in a batch

When you submit a bulk transaction request, Blnk assigns each transaction in the batch its own unique transaction ID and links it to your `batch_id`:

* **`skip_queue: false` (default):** child transactions record `batch_id` in `meta_data.QUEUED_PARENT_TRANSACTION`.
* **`skip_queue: true`:** child transactions set `parent_transaction` to `batch_id`.

<Tabs>
  <Tab title="skip_queue: false">
    Filter on `meta_data.QUEUED_PARENT_TRANSACTION` using [Search via database filtering](/search/db/filtering):

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X POST "http://localhost:5001/transactions/filter" \
        -H "Content-Type: application/json" \
        -H "X-blnk-key: <api-key>" \
        -d '{
          "filters": [
            {
              "field": "meta_data.QUEUED_PARENT_TRANSACTION",
              "operator": "eq",
              "value": "<batch-id>"
            }
          ]
        }'
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.Search.filter(
        {
          filters: [
            {
              field: 'meta_data.QUEUED_PARENT_TRANSACTION',
              operator: 'eq',
              value: '<batch-id>',
            },
          ],
        },
        'transactions',
      );
      ```

      ```go Go wrap theme={"system"}
      results, resp, err := client.Transaction.Filter(blnkgo.FilterParams{
        Filters: []blnkgo.Filter{
          {
            Field:    "meta_data.QUEUED_PARENT_TRANSACTION",
            Operator: blnkgo.OpEqual,
            Value:    "<batch-id>",
          },
        },
      })
      ```
    </CodeGroup>

    ```json Response wrap theme={"system"}
    {
      "data": [
        {
          "transaction_id": "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
          "meta_data": {
            "QUEUED_PARENT_TRANSACTION": "bulk_c62f200b-905f-4983-a349-cadd279234aa"
          },
          "status": "APPLIED"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="skip_queue: true">
    Filter on `parent_transaction`:

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X POST "http://localhost:5001/transactions/filter" \
        -H "Content-Type: application/json" \
        -H "X-blnk-key: <api-key>" \
        -d '{
          "filters": [
            {
              "field": "parent_transaction",
              "operator": "eq",
              "value": "<batch-id>"
            }
          ]
        }'
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.Search.filter(
        {
          filters: [
            {
              field: 'parent_transaction',
              operator: 'eq',
              value: '<batch-id>',
            },
          ],
        },
        'transactions',
      );
      ```

      ```go Go wrap theme={"system"}
      results, resp, err := client.Transaction.Filter(blnkgo.FilterParams{
        Filters: []blnkgo.Filter{
          {
            Field:    "parent_transaction",
            Operator: blnkgo.OpEqual,
            Value:    "<batch-id>",
          },
        },
      })
      ```
    </CodeGroup>

    ```json Response wrap theme={"system"}
    {
      "data": [
        {
          "transaction_id": "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
          "parent_transaction": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
          "status": "APPLIED"
        }
      ]
    }
    ```
  </Tab>
</Tabs>

Replace `<batch-id>` with the `batch_id` from the create response. Use the filter field that matches how you submitted the batch.

***

## Handling inflight bulk transactions

To create inflight bulk transactions, set `inflight: true` in the request body. Blnk ignores any `inflight` flag passed in the transaction objects and respects the root inflight flag.

When set, all transactions in the batch will be processed as `INFLIGHT`.

To commit or void all inflight transactions in a batch, use the `batch_id`. For independently-created inflight holds, see [Bulk commit & void](/transactions/inflight/bulk-update-inflight).

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  curl -X PUT "http://YOUR_BLNK_INSTANCE_URL/transactions/inflight/{batch_id}" \
    -H "X-blnk-key: <api-key>" \
    -H "Content-Type: application/json" \
    -d '{
      "status": "commit"
    }'
  ```

  ```typescript TypeScript wrap theme={"system"}
  const response = await blnk.Transactions.updateStatus('{batch_id}', {
    status: 'commit',
  });
  ```

  ```go Go wrap theme={"system"}
  txn, resp, err := client.Transaction.Update("{batch_id}", blnkgo.UpdateStatus{
    Status: blnkgo.InflightStatusCommit,
  })
  ```
</CodeGroup>

```json Response theme={"system"}
{
  "transaction_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
  "status": "APPLIED",
  "parent_transaction": "bulk_c62f200b-905f-4983-a349-cadd279234aa"
}
```

***

## Webhook notifications

When using `run_async: true`, the API sends webhook notifications upon completion or failure.

The webhooks follow this structure:

```json theme={"system"}
{
  "event": "bulk_transaction.applied",
  "data": {
    "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
    "status": "applied",
    "transaction_count": 4,       
    "error": "error message",     
    "timestamp": "2025-03-02T15:30:45+01:00"
  }
}
```

| **Field**           | **Type** | **Description**                                                                                              |
| :------------------ | :------- | :----------------------------------------------------------------------------------------------------------- |
| `event`             | String   | Name of event. Can be `bulk_transaction.applied`, `bulk_transaction.inflight`, or `bulk_transaction.failed`. |
| `batch_id`          | String   | Specifies the id of the batch transaction.                                                                   |
| `status`            | String   | Status of the batch transaction. Can be `applied`, `inflight`, or `failed`.                                  |
| `transaction_count` | String   | Number of transactions in the batch. Only included for successful cases.                                     |
| `error`             | String   | Error message. Only included for failure cases.                                                              |
| `timestamp`         | String   | Specifies the date & time when the batch transaction was completed.                                          |

<Tabs>
  <Tab title="Successful webhook">
    ```json theme={"system"}
    {
      "event": "bulk_transaction.applied",
      "data": {
        "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
        "status": "applied",
        "transaction_count": 4,
        "timestamp": "2025-03-02T15:30:45+01:00"
      }
    }
    ```
  </Tab>

  <Tab title="Failure webhook">
    ```json theme={"system"}
    {
      "event": "bulk_transaction.failed",
      "data": {
        "batch_id": "bulk_4192d961-5b0e-46ca-bf2f-9386763057f8",
        "status": "failed",
        "error": "failed to queue transaction 2 (Reference: ref_967yg16hhh4q0t0dnsczvud9, Source: bln_a03ef6af-1e5d-46a8-86a9-5fb1f2286f66, Destination: bln_68be0aed-383c-4d27-87db-fb0650093686, Amount: 313333369.18): failed to apply transaction to balances: insufficient funds in source balance. All transactions in this batch have been refunded.",
        "timestamp": "2025-03-02T19:30:22.43597+01:00"
      }
    }
    ```
  </Tab>
</Tabs>

***

## Usage examples

<Tabs>
  <Tab title="Synchronous">
    Process multiple transactions atomically (`atomic: true`, `run_async: false`):

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X POST "http://localhost:5001/transactions/bulk" \
        -H "X-blnk-key: <api-key>" \
        -H "Content-Type: application/json" \
        -d '{
          "atomic": true,
          "inflight": false,
          "transactions": [
            {
              "precise_amount": 10000,
              "precision": 100,
              "reference": "tx_001",
              "description": "First transaction",
              "currency": "NGN",
              "source": "@account1",
              "destination": "@account2"
            },
            {
              "precise_amount": 5000,
              "precision": 100,
              "reference": "tx_002",
              "description": "Second transaction",
              "currency": "NGN",
              "source": "@account2",
              "destination": "@account3"
            }
          ]
        }'
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.Transactions.createBulk({
        atomic: true,
        inflight: false,
        transactions: [
          {
            precise_amount: 10000,
            precision: 100,
            reference: 'tx_001',
            description: 'First transaction',
            currency: 'NGN',
            source: '@account1',
            destination: '@account2',
          },
          {
            precise_amount: 5000,
            precision: 100,
            reference: 'tx_002',
            description: 'Second transaction',
            currency: 'NGN',
            source: '@account2',
            destination: '@account3',
          },
        ],
      });
      ```

      ```go Go wrap theme={"system"}
      batch, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
        Atomic:   true,
        Inflight: false,
        Transactions: []blnkgo.CreateTransactionRequest{
          {
            PreciseAmount: 10000,
            Precision:   100,
            Reference:   "tx_001",
            Description: "First transaction",
            Currency:    "NGN",
            Source:      "@account1",
            Destination: "@account2",
          },
          {
            PreciseAmount: 5000,
            Precision:   100,
            Reference:   "tx_002",
            Description: "Second transaction",
            Currency:    "NGN",
            Source:      "@account2",
            Destination: "@account3",
          },
        },
      })
      ```
    </CodeGroup>

    ```json Response theme={"system"}
    {
      "batch_id": "bulk_4192d961-5b0e-46ca-bf2f-9386763057f8",
      "status": "applied",
      "transaction_count": 2
    }
    ```
  </Tab>

  <Tab title="Asynchronous">
    Process a large batch in the background (`run_async: true`). Blnk returns immediately; the final outcome arrives via [webhook](#webhook-notifications).

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X POST "http://localhost:5001/transactions/bulk" \
        -H "X-blnk-key: <api-key>" \
        -H "Content-Type: application/json" \
        -d '{
          "atomic": true,
          "inflight": false,
          "run_async": true,
          "transactions": [
            {
              "precise_amount": 10000,
              "precision": 100,
              "reference": "tx_001",
              "description": "First transaction",
              "currency": "NGN",
              "source": "@account1",
              "destination": "@account2"
            },
            {
              "precise_amount": 7500,
              "precision": 100,
              "reference": "tx_999",
              "description": "Last transaction",
              "currency": "NGN",
              "source": "@account5",
              "destination": "@account6"
            }
          ]
        }'
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.Transactions.createBulk({
        atomic: true,
        inflight: false,
        run_async: true,
        transactions: [
          {
            precise_amount: 10000,
            precision: 100,
            reference: 'tx_001',
            description: 'First transaction',
            currency: 'NGN',
            source: '@account1',
            destination: '@account2',
          },
          {
            precise_amount: 7500,
            precision: 100,
            reference: 'tx_999',
            description: 'Last transaction',
            currency: 'NGN',
            source: '@account5',
            destination: '@account6',
          },
        ],
      });
      ```

      ```go Go wrap theme={"system"}
      batch, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
        Atomic:   true,
        Inflight: false,
        RunAsync: true,
        Transactions: []blnkgo.CreateTransactionRequest{
          {
            PreciseAmount: 10000,
            Precision:   100,
            Reference:   "tx_001",
            Description: "First transaction",
            Currency:    "NGN",
            Source:      "@account1",
            Destination: "@account2",
          },
          {
            PreciseAmount: 7500,
            Precision:   100,
            Reference:   "tx_999",
            Description: "Last transaction",
            Currency:    "NGN",
            Source:      "@account5",
            Destination: "@account6",
          },
        },
      })
      ```
    </CodeGroup>

    ```json Response theme={"system"}
    {
      "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
      "status": "processing",
      "message": "Bulk transaction processing started"
    }
    ```
  </Tab>

  <Tab title="Inflight">
    <Steps>
      <Step title="Create inflight batch">
        Set `inflight: true` on the bulk request. See [Handling inflight bulk transactions](#handling-inflight-bulk-transactions).

        <CodeGroup>
          ```bash cURL wrap theme={"system"}
          curl -X POST "http://localhost:5001/transactions/bulk" \
            -H "X-blnk-key: <api-key>" \
            -H "Content-Type: application/json" \
            -d '{
              "atomic": true,
              "inflight": true,
              "transactions": [
                {
                  "precise_amount": 10000,
                  "precision": 100,
                  "reference": "tx_003",
                  "description": "First transaction",
                  "currency": "NGN",
                  "source": "@account1",
                  "destination": "@account2"
                },
                {
                  "precise_amount": 5000,
                  "precision": 100,
                  "reference": "tx_004",
                  "description": "Second transaction",
                  "currency": "NGN",
                  "source": "@account2",
                  "destination": "@account3"
                }
              ]
            }'
          ```

          ```typescript TypeScript wrap theme={"system"}
          const response = await blnk.Transactions.createBulk({
            atomic: true,
            inflight: true,
            transactions: [
              {
                precise_amount: 10000,
                precision: 100,
                reference: 'tx_003',
                description: 'First transaction',
                currency: 'NGN',
                source: '@account1',
                destination: '@account2',
              },
              {
                precise_amount: 5000,
                precision: 100,
                reference: 'tx_004',
                description: 'Second transaction',
                currency: 'NGN',
                source: '@account2',
                destination: '@account3',
              },
            ],
          });
          ```

          ```go Go wrap theme={"system"}
          batch, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
            Atomic:   true,
            Inflight: true,
            Transactions: []blnkgo.CreateTransactionRequest{
              {
                PreciseAmount: 10000,
                Precision:   100,
                Reference:   "tx_003",
                Description: "First transaction",
                Currency:    "NGN",
                Source:      "@account1",
                Destination: "@account2",
              },
              {
                PreciseAmount: 5000,
                Precision:   100,
                Reference:   "tx_004",
                Description: "Second transaction",
                Currency:    "NGN",
                Source:      "@account2",
                Destination: "@account3",
              },
            },
          })
          ```
        </CodeGroup>

        ```json Response theme={"system"}
        {
          "batch_id": "bulk_c62f200b-905f-4983-a349-cadd279234aa",
          "status": "inflight",
          "transaction_count": 2
        }
        ```
      </Step>

      <Step title="Commit the batch">
        Use the returned `batch_id` to commit every inflight transaction in the batch:

        <CodeGroup>
          ```bash cURL wrap theme={"system"}
          curl -X PUT "http://localhost:5001/transactions/inflight/bulk_c62f200b-905f-4983-a349-cadd279234aa" \
            -H "X-blnk-key: <api-key>" \
            -H "Content-Type: application/json" \
            -d '{
              "status": "commit"
            }'
          ```

          ```typescript TypeScript wrap theme={"system"}
          const response = await blnk.Transactions.updateStatus(
            'bulk_c62f200b-905f-4983-a349-cadd279234aa',
            { status: 'commit' },
          );
          ```

          ```go Go wrap theme={"system"}
          txn, resp, err := client.Transaction.Update(
            "bulk_c62f200b-905f-4983-a349-cadd279234aa",
            blnkgo.UpdateStatus{
              Status: blnkgo.InflightStatusCommit,
            },
          )
          ```
        </CodeGroup>
      </Step>
    </Steps>
  </Tab>
</Tabs>

***

## Error handling

<Info>
  Structured errors are available from Blnk Core 0.15.0 and later.
</Info>

Bulk endpoints return structured `error_detail` objects for validation failures. For duplicate references and batch rollbacks, behaviour depends on `skip_queue`. See [API error codes](/advanced/error-codes) for the full catalogue.

### Request validation errors

Blnk returns `400` when the request body fails validation before processing starts.

| Code                      | When it happens                          |
| :------------------------ | :--------------------------------------- |
| `TXN_BULK_EMPTY`          | `transactions` array is empty            |
| `TXN_BULK_LIMIT_EXCEEDED` | More than 10,000 items in `transactions` |
| `TXN_VALIDATION_ERROR`    | A transaction object is null or invalid  |

For `TXN_VALIDATION_ERROR`, `error_detail.details.index` identifies the zero-based position in `transactions[]`:

```json 400 Bad Request wrap theme={"system"}
{
  "error_detail": {
    "code": "TXN_VALIDATION_ERROR",
    "message": "transactions[1]: currency: cannot be blank; destination: either destination or destinations is required, not both.",
    "details": {
      "index": 1
    }
  },
  "errors": "transactions[1]: currency: cannot be blank; destination: either destination or destinations is required, not both."
}
```

### Duplicate references

Duplicate-reference handling depends on whether items go through the queue.

<Tabs>
  <Tab title="skip_queue: false">
    On the default queued path, duplicate references are deduplicated in the queue and never recorded. The batch still returns `201` with `status: "applied"` representing the request was successfully submitted:

    ```json 201 Created wrap theme={"system"}
    {
      "batch_id": "bulk_847544fb-2616-43be-a188-f6b8c8f20cf2",
      "status": "applied",
      "transaction_count": 2
    }
    ```

    <Warning>
      `transaction_count` reflects items in the request, not necessarily rows created. A deduplicated item produces no `REJECTED` row and no error.
    </Warning>
  </Tab>

  <Tab title="skip_queue: true">
    When `skip_queue: true`, Blnk validates references inline. A duplicate fails the batch and returns `409` with `TXN_DUPLICATE_REFERENCE`.

    For atomic batches, previously applied items in the same request are refunded via `_refund` child transactions.

    ```json 409 Conflict wrap theme={"system"}
    {
      "batch_id": "bulk_305873c0-518a-470c-bf3a-bc3f3e0b562c",
      "error": "failed to queue transaction 2 (Reference: sqdup-17372-existing, Source: bln_b4819533-9570-4770-a1a0-25c191da0557, Destination: bln_34111155-4015-4e84-bf7c-13a60794bf0b, Amount: 4.00): transaction validation failed: reference sqdup-17372-existing has already been used. All transactions in this batch have been refunded.",
      "error_detail": {
        "code": "TXN_DUPLICATE_REFERENCE",
        "message": "failed to queue transaction 2 (Reference: sqdup-17372-existing, Source: bln_b4819533-9570-4770-a1a0-25c191da0557, Destination: bln_34111155-4015-4e84-bf7c-13a60794bf0b, Amount: 4.00): transaction validation failed: reference sqdup-17372-existing has already been used. All transactions in this batch have been refunded."
      }
    }
    ```
  </Tab>
</Tabs>

***

## Important notes

1. When `atomic` is `true`, transactions are processed in the order provided in the request.
2. Each transaction's `reference` should be unique. On the default queued path, duplicates are silently deduplicated rather than rejected - see [Duplicate references](#duplicate-references).
3. When `run_async` is `true`, processing happens in the background and you'll receive an immediate response with a batch ID.
4. For large transaction batches, using `run_async: true` is recommended to avoid timeout issues.
5. Webhook notifications for async processing contain the same detailed error information as synchronous responses, including rollback status.
6. On the default queued path, `status: "applied"` means the bulk request was accepted - search by `parent_transaction` or `meta_data.QUEUED_PARENT_TRANSACTION` to verify final child statuses.

***

## 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](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx).

<CtaCallout title="Connect your ledger to Blnk Cloud" href="https://cloud.blnkfinance.com/auth/sign-up?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=need-help" buttonLabel="Open Blnk Cloud" trackingEvent="clicked_cloud_signup">
  Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard.
</CtaCallout>
