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

# Midaz with Pix Transaction Routes

> Model reusable Pix transfer flows in Midaz with Transaction Routes and Operation Routes — enforce account and fee rules directly in the ledger.

Every Pix transaction follows a pattern: debit the sender, credit the receiver, and sometimes collect a fee. When that pattern lives only in application code, every team that touches Pix re-implements the same validation logic. Each implementation is another chance for inconsistency.

Transaction Routes move that pattern into the ledger. You define the rules once, and Midaz enforces them on every transaction. The result is a single source of truth for how Pix money flows through your system.

This page walks through two scenarios — a simple peer-to-peer transfer and a transfer with a fee. Each scenario shows how to set up the routes and what your team gains from them.

## Why this matters

***

For **product and operations teams**, Transaction Routes give you auditable Pix flows without application-level enforcement. Every transaction carries a reference to the route it followed, so compliance reviews and incident investigations stay simple.

For **engineering teams**, routes remove repetitive validation code. You configure the account and fee rules once. Midaz then enforces them at the ledger level on every Pix integration.

| Without Routes                                                   | With Routes                                                       |
| ---------------------------------------------------------------- | ----------------------------------------------------------------- |
| Each integration must enforce its own account rules              | Define the rules once, reuse across all Pix transactions          |
| No automatic validation — constraints live in application code   | Midaz rejects transactions that don't match the route's rules     |
| Adding fees requires changes across every Pix integration        | Add a new Operation Route, create a new Transaction Route variant |
| Hard to trace which pattern a transaction was supposed to follow | Every transaction stores its route ID — straightforward to audit  |

For a deeper look at how Transaction Routes and Operation Routes work, see [Accounting Routes](/en/midaz/transaction-routing-entities).

## Prerequisites

***

Both scenarios assume a Midaz environment with the following structure already in place:

| Entity          | Alias             | Type       | Purpose                                           |
| --------------- | ----------------- | ---------- | ------------------------------------------------- |
| Alice's account | `@alice_checking` | `checking` | Sender — Alice's main checking account            |
| Bob's account   | `@bob_checking`   | `checking` | Receiver — Bob's main checking account            |
| BRL asset       | —                 | —          | Brazilian Real, registered as the operating asset |

<Note>
  Values in Midaz are decimal amounts. For BRL, `150.00` means R\$ 150.00.
</Note>

## Scenario 1: Simple Pix transfer

***

Alice sends R\$ 150.00 to Bob via Pix. The money moves from one checking account to another — no fees, no splits, just a clean peer-to-peer transfer.

### The goal

* Debit Alice's checking account by R\$ 150.00
* Credit Bob's checking account by R\$ 150.00
* Validate that both accounts are of type `checking` before processing
* Make this pattern reusable for every Pix transfer between checking accounts

### Setting up the routes

<Steps>
  <Step title="Create the source Operation Route">
    This route defines the debit side of the transfer. The `account_type` rule accepts any account of type `checking` as a source. The route does not hardcode a specific sender.

    ```json theme={null}
    POST /v1/organizations/{org_id}/ledgers/{ledger_id}/operation-routes

    {
      "title": "Pix - Debit Sender",
      "description": "Debits the sender's checking account in a Pix transfer",
      "code": "PIX-SEND-SRC",
      "operationType": "source",
      "account": {
        "ruleType": "account_type",
        "validIf": ["checking"]
      },
      "metadata": {
        "payment_method": "pix",
        "direction": "outbound"
      }
    }
    ```

    Save the returned `id` — you need it when you build the Transaction Route.
  </Step>

  <Step title="Create the destination Operation Route">
    This route defines the credit side. It uses the same rule type: any `checking` account qualifies as a valid receiver.

    ```json theme={null}
    POST /v1/organizations/{org_id}/ledgers/{ledger_id}/operation-routes

    {
      "title": "Pix - Credit Receiver",
      "description": "Credits the receiver's checking account in a Pix transfer",
      "code": "PIX-SEND-DST",
      "operationType": "destination",
      "account": {
        "ruleType": "account_type",
        "validIf": ["checking"]
      },
      "metadata": {
        "payment_method": "pix",
        "direction": "inbound"
      }
    }
    ```
  </Step>

  <Step title="Create the Transaction Route">
    Group both Operation Routes into a single Transaction Route. This route represents "Pix Transfer" in your system.

    ```json theme={null}
    POST /v1/organizations/{org_id}/ledgers/{ledger_id}/transaction-routes

    {
      "title": "Pix Transfer",
      "description": "Standard Pix transfer between two checking accounts",
      "operationRoutes": [
        "<pix-send-src-id>",
        "<pix-send-dst-id>"
      ],
      "metadata": {
        "payment_rail": "pix",
        "spi_message_type": "pacs.008",
        "regulation": "BCB_PIX"
      }
    }
    ```

    Replace the placeholder IDs with the actual Operation Route IDs from the previous steps.
  </Step>
</Steps>

### Executing a Pix transfer

With the route in place, every Pix transfer references the Transaction Route ID in the `routeId` field. Midaz validates that the accounts match the route's rules before it processes the transaction.

```json theme={null}
POST /v1/organizations/{org_id}/ledgers/{ledger_id}/transactions/json

{
  "chartOfAccountsGroupName": "PIX",
  "description": "Pix transfer from Alice to Bob",
  "code": "PIX-20260306-001",
  "routeId": "<pix-transfer-route-id>",
  "send": {
    "asset": "BRL",
    "value": "150.00",
    "source": {
      "from": [
        {
          "accountAlias": "@alice_checking",
          "amount": { "asset": "BRL", "value": "150.00" },
          "description": "Pix sent to Bob",
          "routeId": "<pix-send-src-id>"
        }
      ]
    },
    "distribute": {
      "to": [
        {
          "accountAlias": "@bob_checking",
          "amount": { "asset": "BRL", "value": "150.00" },
          "description": "Pix received from Alice",
          "routeId": "<pix-send-dst-id>"
        }
      ]
    }
  },
  "metadata": {
    "pix_end_to_end_id": "E123456782026030614300000000001",
    "pix_key_type": "cpf",
    "pix_key": "123.456.789-00"
  }
}
```

### What happens under the hood

<Steps>
  <Step title="Midaz receives the transaction">
    The request carries the Transaction Route ID in the `routeId` field. Midaz loads the route configuration.
  </Step>

  <Step title="Source validation">
    For each `from` entry, Midaz checks the account against the source Operation Route rules. Alice's account is type `checking`, so it matches the `account_type` rule. Validation passes.
  </Step>

  <Step title="Destination validation">
    For each `to` entry, Midaz checks the account against the destination Operation Route rules. Bob's account is type `checking`, so validation passes.
  </Step>

  <Step title="Midaz processes the transaction">
    Both validations pass, so Midaz creates the transaction atomically. It debits `@alice_checking` by R\$ 150.00 and credits `@bob_checking` by R\$ 150.00.
  </Step>
</Steps>

<Tip>
  If Alice sends from a `savings` account instead, Midaz rejects the transaction. The route accepts only `checking` accounts as sources, and you write no application-side validation.
</Tip>

## Scenario 2: Pix transfer with fee collection

***

This flow matches Scenario 1, but now the bank charges a R\$ 1.50 fee on each Pix transfer. The flow adds a third Operation Route for the fee destination, and Alice's total debit rises to R\$ 151.50.

### What changes

You already have the source and destination Operation Routes from Scenario 1. You add one Operation Route for the fee and a new Transaction Route that groups all three.

| Entity              | Alias               | Type      | Purpose                                          |
| ------------------- | ------------------- | --------- | ------------------------------------------------ |
| Fee revenue account | `@revenue_pix_fees` | `revenue` | Internal account that collects Pix transfer fees |

### Setting up the fee route

<Steps>
  <Step title="Create the fee Operation Route">
    The previous routes use `account_type`. This one uses the `alias` rule type instead. It targets one specific account — `@revenue_pix_fees` — and no other account qualifies.

    ```json theme={null}
    POST /v1/organizations/{org_id}/ledgers/{ledger_id}/operation-routes

    {
      "title": "Pix - Fee Collection",
      "description": "Credits the bank's revenue account with the Pix transfer fee",
      "code": "PIX-FEE-DST",
      "operationType": "destination",
      "account": {
        "ruleType": "alias",
        "validIf": "@revenue_pix_fees"
      },
      "metadata": {
        "fee_type": "pix_transfer_fee"
      }
    }
    ```
  </Step>

  <Step title="Create the Transaction Route with fee">
    This route groups the original source and destination routes with the new fee route. It is a separate Transaction Route from the simple transfer, so your system can offer both variants.

    ```json theme={null}
    POST /v1/organizations/{org_id}/ledgers/{ledger_id}/transaction-routes

    {
      "title": "Pix Transfer with Fee",
      "description": "Pix transfer between checking accounts with fee collection",
      "operationRoutes": [
        "<pix-send-src-id>",
        "<pix-send-dst-id>",
        "<pix-fee-dst-id>"
      ],
      "metadata": {
        "payment_rail": "pix",
        "includes_fee": true
      }
    }
    ```
  </Step>
</Steps>

### Executing a Pix transfer with fee

Alice sends R\$ 150.00 to Bob. The bank collects R\$ 1.50. Alice's total debit is R\$ 151.50.

```json theme={null}
POST /v1/organizations/{org_id}/ledgers/{ledger_id}/transactions/json

{
  "chartOfAccountsGroupName": "PIX",
  "description": "Pix transfer from Alice to Bob (with fee)",
  "code": "PIX-20260306-002",
  "routeId": "<pix-transfer-with-fee-route-id>",
  "send": {
    "asset": "BRL",
    "value": "151.50",
    "source": {
      "from": [
        {
          "accountAlias": "@alice_checking",
          "amount": { "asset": "BRL", "value": "151.50" },
          "description": "Pix sent to Bob + transfer fee",
          "routeId": "<pix-send-src-id>"
        }
      ]
    },
    "distribute": {
      "to": [
        {
          "accountAlias": "@bob_checking",
          "amount": { "asset": "BRL", "value": "150.00" },
          "description": "Pix received from Alice",
          "routeId": "<pix-send-dst-id>"
        },
        {
          "accountAlias": "@revenue_pix_fees",
          "amount": { "asset": "BRL", "value": "1.50" },
          "description": "Pix transfer fee",
          "routeId": "<pix-fee-dst-id>"
        }
      ]
    }
  },
  "metadata": {
    "pix_end_to_end_id": "E123456782026030614300000000002",
    "fee_amount": "1.50"
  }
}
```

**Result:** Midaz debits Alice R\$ 151.50. Bob receives R\$ 150.00. The bank collects R\$ 1.50. All in a single atomic transaction — fully balanced, fully auditable.

### What this unlocks

* **Transparent fee collection** — the fee is a first-class ledger entry, not hidden metadata. Finance and compliance teams see exactly where the R\$ 1.50 went.
* **Reusable building blocks** — the simple and fee variants share the source and destination Operation Routes. You add only what changes.
* **Route-level control** — your system can offer both "Pix Transfer" and "Pix Transfer with Fee" as distinct products, each backed by its own Transaction Route.
* **Easy evolution** — to add a percentage-based fee or a split across revenue accounts, create new Operation Routes and compose a new Transaction Route. Existing flows stay untouched.

## Understanding rule types

***

The two rule types serve different purposes. The right choice depends on whether the account in a route is dynamic or fixed.

| Rule type      | `validIf` format                                                         | Behavior                                                    | When to use                                                                                   |
| -------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `account_type` | **Array of strings** — e.g., `["checking"]` or `["checking", "savings"]` | Accepts any account that matches one of the specified types | Dynamic participants — the sender or receiver can be any account of that type                 |
| `alias`        | **String** — e.g., `"@revenue_pix_fees"`                                 | Must target a specific account by its alias                 | Fixed participants — the route always hits the same account, like a fee or settlement account |

<Tip>
  You can combine both rule types within a single Transaction Route. Scenario 2 does exactly that: `account_type` for the dynamic sender and receiver, `alias` for the fixed fee account.
</Tip>

## What you need to get started

***

| Requirement                 | Details                                                                                                                                                              |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Midaz** (v3.x.x+)         | Core ledger with Transaction Route validation enabled                                                                                                                |
| **Route validation config** | Enable route validation via the Ledger Settings API: `PATCH /v1/organizations/{org_id}/ledgers/{ledger_id}/settings` with `{"accounting": {"validateRoutes": true}}` |
| **Accounts and asset**      | At minimum: two customer accounts and a BRL asset registered in the ledger                                                                                           |
| **Operation Routes**        | One per operation leg (source, destination, fee)                                                                                                                     |
| **Transaction Route**       | Groups the Operation Routes into a reusable pattern                                                                                                                  |

<Note>
  You must enable Transaction Route validation for each ledger. See [Working with Accounting Routes](/en/midaz/transaction-routing-entities#working-with-accounting-routes) for the configuration steps.
</Note>

## Next steps

***

<CardGroup>
  <Card title="Accounting Routes" icon="route" href="/en/midaz/transaction-routing-entities">
    Understand how Operation Routes and Transaction Routes work at a deeper level.
  </Card>

  <Card title="Transactions" icon="arrow-right-arrow-left" href="/en/midaz/transactions">
    Learn about Midaz's double-entry transaction model and N:N capabilities.
  </Card>

  <Card title="Pix with automated fees" icon="calculator" href="/en/rails/pix/midaz-for-pix-with-fees">
    Combine the Pix Plugin with the Fees Engine for automated fee management.
  </Card>

  <Card title="Pix Switch" icon="money-bill-transfer" href="/en/rails/pix/pix-switch">
    Explore the full Pix Plugin architecture and connection models.
  </Card>
</CardGroup>
