> ## 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.

# 0.10.5 Migration Guide

> Migration guide for upgrading to Blnk v0.10.5, covering bulk transactions defaulting to the queue.

export const RelatedTopics = ({title = "Related topics", items = []}) => {
  if (!items.length) {
    return null;
  }
  return <nav className="related-topics not-prose mt-20 mb-10 flex flex-col" aria-label={title}>
      <p className="related-topics-heading m-0 border-b border-zinc-200 pb-3 text-sm font-medium text-zinc-500 dark:border-white/10 dark:text-zinc-400">
        {title}
      </p>
      <ul className="related-topics-list m-0 mt-3 flex list-none flex-col gap-0.5 p-0">
        {items.map(item => {
    const isExternal = typeof item.href === "string" && (/^https?:\/\//i).test(item.href);
    return <li key={item.href} className="m-0 p-0">
              <a href={item.href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} className="related-topics-link group inline-flex items-center gap-2 text-sm font-semibold text-zinc-700 no-underline transition-colors dark:text-zinc-300">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="related-topics-icon shrink-0 text-zinc-400 dark:text-zinc-500" aria-hidden="true">
                  <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
                  <path d="M14 2v4a2 2 0 0 0 2 2h4" />
                  <path d="M10 9H8" />
                  <path d="M16 13H8" />
                  <path d="M16 17H8" />
                </svg>
                <span className="relative top-px transition-colors group-hover:text-[#DD7B1B]">
                  {item.title}
                </span>
              </a>
            </li>;
  })}
      </ul>
    </nav>;
};

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>;
};

This migration guide covers the breaking change introduced in Blnk v0.10.5 for [bulk transactions](/transactions/bulk-transactions). Bulk create now uses the queue by default instead of processing every item synchronously in the request.

If you are upgrading from **v0.10.4 or earlier** and your app expects balances or final statuses to update before the bulk create response returns, review this guide before you upgrade.

<Warning>
  Do not treat a successful bulk create response as proof that every child is already applied. On the default queued path, the response means the batch was **accepted for processing**.

  If your app calls an external provider or releases funds based on that response alone, you can open a race where money moves before the ledger finishes.
</Warning>

***

## Recommended upgrade

If you run **0.10.5 or later** on the queued bulk path, upgrade to **Blnk Core 0.15.0 or later**.

From 0.15.0, Core migrations add expression indexes on `meta_data->>'QUEUED_PARENT_TRANSACTION'` and related queue hot paths. Without those indexes, commit, refund, and parent lookups can fall back to sequential scans and drain the queue slowly under load.

Prefer upgrading over staying on 0.10.5–0.14.x with a hand-applied index.

If you cannot upgrade yet, create the index manually on your Postgres database:

```sql theme={"system"}
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_transactions_queued_parent
ON blnk.transactions ((meta_data->>'QUEUED_PARENT_TRANSACTION'));
```

<Note>
  `CREATE INDEX CONCURRENTLY` cannot run inside a transaction block. Run it as a standalone statement. On large tables it can take time, but it avoids locking writes while the index builds.
</Note>

Core 0.15.0 applies an equivalent index as `idx_txn_meta_queued_parent` (partial, where the key is present) plus related queue indexes. After you upgrade, you do not need to keep a separate hand-created index unless your operator process requires it.

***

## Breaking changes summary

* **From:** Bulk create always processed items synchronously (`skip_queue` effectively `true`).
* **To:** Bulk create defaults to `skip_queue: false` and enqueues each item.
* **Impact:** A successful bulk response no longer guarantees that every child transaction is already `APPLIED` or `INFLIGHT` on balances. Parent linking and duplicate-reference behaviour also follow the queued path unless you opt out.

***

## What changed

1. **Default queueing:** Bulk create now respects `skip_queue` and defaults it to `false`. Before 0.10.5, Blnk forced synchronous processing inside the batch.
2. **Response meaning:** On the default path, `status: "applied"` means the batch was **successfully submitted**, not that every child has already moved balances.
3. **Parent linking:** With `skip_queue: false`, children store `batch_id` in `meta_data.QUEUED_PARENT_TRANSACTION`. With `skip_queue: true`, children set `parent_transaction` to `batch_id`.
4. **Duplicate references:** On the queued path, duplicate references are deduplicated silently and the batch can still return `201`. On `skip_queue: true`, duplicates fail the batch with `409`.

See [Bulk transactions](/transactions/bulk-transactions) for current behaviour.

***

## Who is affected?

Review this change if your integration:

* Reads balances or child transaction statuses immediately after bulk create
* Treats bulk `status: "applied"` as proof that every item is final
* Looks up batch children only by `parent_transaction`
* Relies on synchronous `409` failures for duplicate references inside a bulk request

If none of these apply, you can upgrade without changing your bulk requests.

***

## Migration options

Choose one path based on what your next step needs.

| Need                                  | What to do                                                                                 |
| :------------------------------------ | :----------------------------------------------------------------------------------------- |
| Keep pre-0.10.5 synchronous behaviour | Send `"skip_queue": true` on every bulk create                                             |
| Adopt the new default queue path      | Omit `skip_queue` or set `"skip_queue": false`, then confirm child outcomes asynchronously |

### Option 1: Keep synchronous processing

Add `"skip_queue": true` to restore the previous inline behaviour.

Use this when the next step in your app needs the ledger outcome before the HTTP call returns (for example, show an updated balance, fail the request with the batch, or chain another post that depends on these balances).

<CodeGroup>
  ```bash cURL wrap {5} theme={"system"}
  curl -X POST "http://YOUR_BLNK_INSTANCE_URL/transactions/bulk" \
    -H "X-blnk-key: <api-key>" \
    -H "Content-Type: application/json" \
    -d '{
      "atomic": true,
      "inflight": false,
      "run_async": false,
      "skip_queue": true,
      "transactions": [
        {
          "amount": 1000,
          "precision": 100,
          "reference": "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
          "currency": "USD",
          "source": "@World",
          "destination": "bln_ebcd1a29-8673-4d31-b410-010add942bec",
          "description": "Payroll credit",
          "allow_overdraft": true
        },
        {
          "amount": 500,
          "precision": 100,
          "reference": "ref_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30",
          "currency": "USD",
          "source": "bln_ebcd1a29-8673-4d31-b410-010add942bec",
          "destination": "bln_7b8a2c1d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
          "description": "Payroll split"
        }
      ]
    }'
  ```

  ```typescript TypeScript wrap {5} theme={"system"}
  const response = await blnk.Transactions.createBulk({
    atomic: true,
    inflight: false,
    run_async: false,
    skip_queue: true,
    transactions: [
      {
        amount: 1000,
        precision: 100,
        reference: 'ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94',
        currency: 'USD',
        source: '@World',
        destination: 'bln_ebcd1a29-8673-4d31-b410-010add942bec',
        description: 'Payroll credit',
        allow_overdraft: true,
      },
      {
        amount: 500,
        precision: 100,
        reference: 'ref_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30',
        currency: 'USD',
        source: 'bln_ebcd1a29-8673-4d31-b410-010add942bec',
        destination: 'bln_7b8a2c1d-4e5f-6a7b-8c9d-0e1f2a3b4c5d',
        description: 'Payroll split',
      },
    ],
  });
  ```

  ```go Go wrap {5} theme={"system"}
  result, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
      Atomic: true,
      Inflight: false,
      RunAsync: false,
      SkipQueue: true,
      Transactions: []blnkgo.CreateTransactionRequest{
          {
              Amount: 1000,
              Precision: 100,
              Reference: "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94",
              Currency: "USD",
              Source: "@World",
              Destination: "bln_ebcd1a29-8673-4d31-b410-010add942bec",
              Description: "Payroll credit",
              AllowOverdraft: true,
          },
          {
              Amount: 500,
              Precision: 100,
              Reference: "ref_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30",
              Currency: "USD",
              Source: "bln_ebcd1a29-8673-4d31-b410-010add942bec",
              Destination: "bln_7b8a2c1d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
              Description: "Payroll split",
          },
      },
  })
  ```
</CodeGroup>

With `skip_queue: true`:

* Child statuses and balances update in the same request when `run_async` is `false`.
* Look up children with `parent_transaction = batch_id`.
* Duplicate references fail the batch with `409`.

### Option 2: Adopt the queued default

Omit `skip_queue` or set `"skip_queue": false`. Then update your confirmation path:

1. Save `batch_id` from the create response.
2. Do not treat `status: "applied"` as final ledger success for every child.
3. Confirm child outcomes by filtering on `meta_data.QUEUED_PARENT_TRANSACTION`, or wait for `bulk_transaction.applied`, `bulk_transaction.inflight`, or `bulk_transaction.failed` when you use `run_async: true`.

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

<Warning>
  On the queued path, `transaction_count` reflects items in the request, not necessarily rows created. Duplicate references can be deduplicated without a `REJECTED` row or error response.
</Warning>

***

## Migration checklist

* Prefer upgrading to **0.15.0 or later** so queue parent indexes are applied by migration.
* If you must stay below 0.15.0 for now, create `idx_transactions_queued_parent` manually (see [Recommended upgrade](#recommended-upgrade)).
* Decide per bulk call whether you need sync (`skip_queue: true`) or queue (`skip_queue: false`).
* Update any code that assumes balances change, or that an external payout is safe, before the bulk create response returns.
* Confirm child outcomes with webhooks or by filtering on `meta_data.QUEUED_PARENT_TRANSACTION` before you call external providers or release funds.
* Update batch child lookups to use `meta_data.QUEUED_PARENT_TRANSACTION` on the default path, or `parent_transaction` when you set `skip_queue: true`.
* Revisit duplicate-reference handling if you previously relied on synchronous `409` failures.
* For large batches, prefer `run_async: true` and handle bulk webhooks.

***

<Note>
  This migration guide covers the bulk queueing change in Blnk v0.10.5 and the follow-on queue index fix in v0.15.0. For other features and fixes, see the [release notes](/changelog/blnk-core).
</Note>

***

## 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>

<RelatedTopics
  items={[
{ title: "Bulk transactions", href: "/transactions/bulk-transactions" },
{ title: "Handling concurrency", href: "/guides/concurrency" },
{ title: "v15 migration", href: "/changelog/v15-migration" },
{ title: "Install", href: "/home/install" },
]}
/>
