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

# Balance Overdraft

> Enable controlled Balance Overdraft in Midaz with automatic split operations, credit repayment priority, and limits for BNPL or settlement accounts.

Balance Overdraft lets you debit a balance beyond its available funds. The primary balance never goes negative. Midaz tracks the deficit as **OverdraftUsed** and splits the operation between the primary balance and an internal companion balance. When credits arrive, Midaz repays the overdraft first. Any remainder flows to Available.

This mechanism supports credit lines, BNPL, settlement accounts, earned wage access, and any product that needs controlled negative positions.

## Balance direction

***

Balances carry a `direction` field that defines how debits and credits affect the balance:

| Direction          | Behavior                          | Typical use                          |
| ------------------ | --------------------------------- | ------------------------------------ |
| `credit` (default) | Debit decreases, credit increases | Checking accounts, wallets, reserves |
| `debit`            | Debit increases, credit decreases | Loans, overdraft tracking, payables  |

<Note>
  You set direction at creation time. It is **immutable**. The overdraft companion balance (described below) always uses `direction=debit`.
</Note>

## Balance settings

***

The `settings` object on a balance controls overdraft behavior:

| Field                   | Type             | Description                                                                                          |
| ----------------------- | ---------------- | ---------------------------------------------------------------------------------------------------- |
| `allowOverdraft`        | boolean          | Enables overdraft on this balance                                                                    |
| `overdraftLimitEnabled` | boolean          | Gates whether a limit is enforced                                                                    |
| `overdraftLimit`        | string (decimal) | Maximum overdraft amount. Required when `overdraftLimitEnabled` is `true`. Must be greater than `0`. |

<Note>
  The `settings` object also carries a **system-managed** `balanceScope` field. It separates internal balances (like the overdraft companion) from transactional balances. You do not set this field directly.
</Note>

## Configuration modes

***

### No overdraft (default)

The standard behavior. Midaz rejects any debit that exceeds the available balance.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "checking",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": false
    }
  }
  ```
</CodeGroup>

### Unlimited overdraft

The balance can go negative without a cap. Use this for settlement or pool accounts, where negative positions are normal and you reconcile them externally.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "settlement",
    "assetCode": "USD",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": false
    }
  }
  ```
</CodeGroup>

### Limited overdraft

The balance can go negative up to a defined limit. The most common mode for consumer credit products.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "checking",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "5000.00"
    }
  }
  ```
</CodeGroup>

<Warning>
  When `overdraftLimitEnabled` is `true`, you must set `overdraftLimit` to a positive decimal string. If you omit it or set it to `"0"`, Midaz returns error `0172 - ErrInvalidBalanceSettings`.
</Warning>

## How overdraft works

***

### Operation split

When a debit transaction exceeds the available funds, Midaz automatically splits the operation:

1. The debit consumes all remaining Available and floors it at **0**.
2. Midaz accrues the excess as **OverdraftUsed** on the primary balance.
3. Midaz creates a companion operation on the internal `"overdraft"` balance (described below). This operation records the liability as a double-entry debit.

**Example:** Balance has Available = 300. A debit of 500 arrives.

| Step   | Available | OverdraftUsed | Description                                           |
| ------ | --------- | ------------- | ----------------------------------------------------- |
| Before | 300       | 0             | Normal state                                          |
| After  | 0         | 200           | 300 consumed from Available, 200 accrued as overdraft |

The transaction succeeds as a single atomic operation. The caller does not need to handle the split — Midaz does it automatically.

<Note>
  If you configure a limit, Midaz checks the resulting OverdraftUsed against `overdraftLimit` **before** it processes the transaction. If the result exceeds the limit, Midaz rejects the transaction with error `0167 - ErrOverdraftLimitExceeded`.
</Note>

### Automatic repayment (refund split)

When a credit arrives and `OverdraftUsed > 0`, Midaz prioritizes repayment:

1. Midaz applies the credit to **OverdraftUsed** first and reduces the debt.
2. Any remaining amount after OverdraftUsed reaches 0 flows to **Available**.
3. A companion operation on the `"overdraft"` balance records the repayment.

**Example:** OverdraftUsed = 200, Available = 0. A credit of 350 arrives.

| Step   | Available | OverdraftUsed | Description                       |
| ------ | --------- | ------------- | --------------------------------- |
| Before | 0         | 200           | Overdraft active                  |
| After  | 150       | 0             | 200 repaid, 150 goes to Available |

<Tip>
  Repayment is automatic. You cannot bypass it. Midaz reduces overdraft positions as early as possible, which keeps the balance healthy.
</Tip>

### Cancelling a pending overdraft transaction

When you cancel a `PENDING` transaction that drew overdraft, Midaz keeps the companion balance in step with the primary balance:

1. The cancel reverses the original hold and any overdraft drawn during the pending window. `OverdraftUsed` returns to its value before the hold.
2. A companion `CREDIT` operation on the `"overdraft"` balance shrinks the liability by the exact amount drawn.
3. Midaz applies the primary cancel and the companion credit in the **same atomic batch**. The two balances never drift out of sync.

Midaz keeps the companion balance in step with the primary balance. This holds across the hold, commit, and cancel phases of any pending transaction that touches overdraft.

## Position

***

Every balance response includes a computed `position` block. It gives a real-time view of the balance state:

| Field                     | Description                                                                                                                          |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `available`               | `Balance.Available` minus `OverdraftUsed`. Can be negative when overdraft is active.                                                 |
| `onHold`                  | Mirrors `Balance.OnHold` — funds reserved by pending operations.                                                                     |
| `overdraftLimitAvailable` | Remaining overdraft headroom. `"0"` when overdraft is disabled. Omitted when unlimited. Positive decimal when a limit is configured. |

<Warning>
  Do not cache the `position` block for accounting purposes. Midaz never persists it — it computes the block at query time from the current balance state.
</Warning>

## Companion balance

***

When you update a balance to set `allowOverdraft` to `true` for the first time, Midaz auto-provisions a **companion balance** under the same account. The companion balance records the liability side of the double-entry. Midaz creates it once per account and reuses it across every overdraft draw and repayment.

| Property         | Value         | Why                                                                    |
| ---------------- | ------------- | ---------------------------------------------------------------------- |
| `key`            | `"overdraft"` | Reserved system key                                                    |
| `direction`      | `debit`       | The companion tracks a liability — debits grow it, credits shrink it   |
| `scope`          | `internal`    | Blocks direct user operations                                          |
| `allowSending`   | `true`        | Required for DEBIT operations on the companion (overdraft draws)       |
| `allowReceiving` | `true`        | Required for CREDIT operations on the companion (overdraft repayments) |

This balance is **fully system-managed**:

* You cannot create, modify, or delete it through the public API.
* Midaz **reserves** the key `"overdraft"`. A request that creates a balance with this key returns error `0170 - ErrReservedBalanceKey`.
* It mirrors the liability as a proper double-entry record, so the ledger stays balanced.

<Note>
  The `scope: "internal"` value blocks direct user operations, regardless of the permission flags above. Midaz rejects any direct operation on this balance with error `0168 - ErrDirectOperationOnInternalBalance`. The companion moves only through system-driven overdraft enrichment.
</Note>

## Overdraft state on operations

***

Every operation exposes the overdraft state on the `balance` and `balanceAfter` blocks. The `overdraftUsed` field records the overdraft consumed before and after the operation. This gives a complete audit trail without a separate balance query.

For operations that do not touch overdraft, both values are `"0"`.

System-managed companion operations on the `"overdraft"` balance use `type: "OVERDRAFT"` (uppercase). The `direction` field carries the lifecycle: `"debit"` for a draw, `"credit"` for a repayment.

<CodeGroup>
  ```json Primary debit drawing overdraft theme={null}
  {
    "type": "DEBIT",
    "direction": "debit",
    "amount": { "value": "500" },
    "accountAlias": "@user123",
    "balanceKey": "checking",
    "balance": {
      "available": "300",
      "onHold": "0",
      "version": 1,
      "overdraftUsed": "0"
    },
    "balanceAfter": {
      "available": "0",
      "onHold": "0",
      "version": 2,
      "overdraftUsed": "200"
    }
  }
  ```

  ```json Companion overdraft draw theme={null}
  {
    "type": "OVERDRAFT",
    "direction": "debit",
    "amount": { "value": "200" },
    "balanceKey": "overdraft",
    "balance": {
      "available": "0",
      "onHold": "0",
      "version": 1,
      "overdraftUsed": "0"
    },
    "balanceAfter": {
      "available": "200",
      "onHold": "0",
      "version": 2,
      "overdraftUsed": "200"
    }
  }
  ```
</CodeGroup>

Both the primary and the companion operation share the same `overdraftUsed` before/after pair. They mirror the primary balance's overdraft transition, so the lifecycle is visible from either row. The internal `snapshot` JSONB column on the `operations` table stores the same values for indexing and historical reconstruction. This column is not part of the public JSON wire. The values surface on `balance.overdraftUsed` and `balanceAfter.overdraftUsed` instead. Midaz can add future system-generated context to the snapshot without breaking the public contract.

<Tip>
  Companion operations inherit the primary operation's `routeId`. Midaz resolves their `routeCode` and `routeDescription` independently from the route's `overdraft` rubric. If the route has no overdraft entry, both stay empty.
</Tip>

## Overdraft events

***

Midaz publishes lifecycle events to RabbitMQ when overdraft state changes. Publication is **enabled by default**. Set the flag to `"false"` to opt out.

<CodeGroup>
  ```bash Environment theme={null}
  # Disable overdraft event publication (default: enabled).
  RABBITMQ_OVERDRAFT_EVENTS_ENABLED=false

  # Optional: route overdraft events to a dedicated exchange.
  # When unset, the broker's default exchange is used.
  RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE=transaction.overdraft_events.exchange
  ```
</CodeGroup>

### Event types

| Event               | Description                                                              |
| ------------------- | ------------------------------------------------------------------------ |
| `overdraft.drawn`   | Overdraft was consumed — OverdraftUsed increased                         |
| `overdraft.repaid`  | Overdraft was partially repaid — OverdraftUsed decreased but remains > 0 |
| `overdraft.cleared` | Overdraft was fully repaid — OverdraftUsed reached 0                     |

### Example event payload

<CodeGroup>
  ```json JSON expandable theme={null}
  {
    "source": "midaz",
    "eventType": "balance",
    "action": "overdraft.drawn",
    "timestamp": "2026-04-28T14:30:00.000000Z",
    "version": "v3.0.0",
    "organizationId": "0198575d-f9fd-702b-bb15-fa4c980b32c7",
    "ledgerId": "0198575d-fa0b-7ac7-8b7d-9d3ab7dccafc",
    "payload": {
      "accountId": "0198575f-a8f9-7924-a6d7-8122f2c77ddd",
      "transactionId": "019b2c3d-4e5f-6789-0123-456789abcdef",
      "amount": "200",
      "overdraftBalance": "200",
      "overdraftLimit": "5000.00",
      "timestamp": "2026-04-28T14:30:00.000000Z"
    }
  }
  ```
</CodeGroup>

<Tip>
  Use overdraft events to trigger downstream workflows — interest accrual, customer notifications, risk alerts, or automatic collection processes.
</Tip>

## Use cases

***

### Checking account overdraft (cheque especial)

Classic consumer credit. The customer's checking balance can go negative up to a pre-approved limit. Interest accrues on the outstanding amount.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "checking",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "2000.00"
    }
  }
  ```
</CodeGroup>

### Buy Now, Pay Later (BNPL)

A BNPL provider issues a purchase credit against the customer's balance. This creates an immediate overdraft position that the customer repays in installments.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "bnpl",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "10000.00"
    }
  }
  ```
</CodeGroup>

### Earned Wage Access / Salary advance

Employees draw against future earnings. Payroll credits clear the overdraft position when they arrive.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "salary-advance",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "3000.00"
    }
  }
  ```
</CodeGroup>

### Marketplace receivables advance

Sellers receive an advance on future receivables. Midaz repays the overdraft automatically as sales settlements arrive.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "receivables",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "50000.00"
    }
  }
  ```
</CodeGroup>

### Settlement / Pool accounts (unlimited mode)

Settlement and pool accounts routinely go negative during intraday processing. Unlimited overdraft avoids artificial rejections while you reconcile the position by end-of-day.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "settlement-pool",
    "assetCode": "USD",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": false
    }
  }
  ```
</CodeGroup>

### Revolving credit lines (B2B)

Businesses draw and repay from a revolving credit facility. The overdraft limit represents the total credit line.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "credit-line",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "500000.00"
    }
  }
  ```
</CodeGroup>

### Insurance pre-financing

Insurers pre-finance claims before premium collection cycles close. The overdraft covers the gap between payout and collection.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "claims-prefin",
    "assetCode": "BRL",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "100000.00"
    }
  }
  ```
</CodeGroup>

### Loyalty programs (advanced points)

Customers redeem points before they earn them. The overdraft tracks the point deficit and clears as customers accrue new points.

<CodeGroup>
  ```json JSON theme={null}
  {
    "key": "loyalty-points",
    "assetCode": "POINTS",
    "settings": {
      "allowOverdraft": true,
      "overdraftLimitEnabled": true,
      "overdraftLimit": "10000"
    }
  }
  ```
</CodeGroup>

## Protection rules

***

Overdraft introduces several immutability and access constraints to maintain ledger integrity:

* **Direction is immutable.** Once you set a balance's `direction` at creation, you cannot change it.
* **Internal balances block writes.** You cannot create, delete, or update the `"overdraft"` companion balance through the public API — a PATCH returns error `0175`.
* **Reserved keys.** Midaz reserves the key `"overdraft"` for the system-managed companion balance.
* **Disabling overdraft preserves outstanding debt.** You can set `allowOverdraft: false` while `OverdraftUsed > 0` to block future draws, while incoming credits still repay the existing debt.
* **Limit cannot drop below usage.** If `OverdraftUsed = 200`, Midaz rejects `overdraftLimit: "100"` with error `0173`, so repay below the new ceiling first or set a higher limit.
* **Optimistic concurrency.** Balance updates use version-based concurrency control, and Midaz rejects a stale write with error `0174` — retry with the latest version.

For the complete catalog of overdraft-related error codes (0167–0175), see the [Midaz error list](/en/reference/midaz/error-list).

## Next steps

***

* Learn about [Balances](/en/midaz/balances) — the foundation that overdraft builds on.
* Understand [Operations](/en/midaz/operations) to trace how overdraft splits appear in the ledger.
* Set up the [Event Publisher](/en/midaz/event-publisher) to consume overdraft lifecycle events.
* Explore [Transactions](/en/midaz/transactions) for the full picture of double-entry accounting in Midaz.
