Transactions

A Transaction in Midaz represents a complete financial event, often involving multiple accounts. At the heart of Midaz is a powerful double-entry accounting system that ensures every financial movement is precise, reliable, and balanced.

Double-entry accounting

The double-entry system operates on a simple yet transformative principle: for every transaction, there are two corresponding entries—a debit and a credit. This framework ensures that all financial activities are recorded holistically, providing a complete and balanced view of your accounts.

Each transaction impacts two accounts, maintaining equilibrium in the financial ecosystem:

  • Debits reflect the value received or resources consumed.
  • Credits indicate the value given or resources provided.

For organizations leveraging Midaz’s advanced transaction capabilities, the double-entry system seamlessly integrates with our architecture. Our platform ensures that every debit and credit is automatically tracked and balanced, streamlining your financial processes.

Example

Consider this example (Figure 1): you perform a transaction to transfer R$1000 from one account to another. This process will have two operations:

  • One operation to debit R$ 1,000.00 from the source account.
  • One operation to credit R$ 1,000.00 to the destination account.
The image shows a simple flowchart representing a financial transaction of transferring BRL 1,000.00. It is enclosed in a rectangle labeled "Transfer Transaction BRL 1,000.00" and contains two operations: a red "Debit Operation" box on the left with the text "@source_account - BRL 1,000.00," and a green "Credit Operation" box on the right with the text "@destination_account + BRL 1,000.00." An arrow points from the debit operation to the credit operation, indicating the flow of the transaction.

Figure 1. Example of the Operations in a transaction to transfer BRL 1.000,00.

In Midaz, these entries are automatically captured, ensuring precision and alignment with your financial strategy. Our platform’s intuitive design enables you to view and analyze these movements effortlessly, promoting a culture of financial autonomy.

N:N Transactions (Many-to-Many)

Unlike traditional financial systems that limit transactions to one-to-one or one-to-many relationships, Midaz enables N:N transactions, allowing multiple source and destination accounts within a single transaction.

Consider a marketplace payout where multiple sellers (several source accounts) are paid from a single escrow account, and each seller might also pay a fee to the platform. This could be modeled as one transaction involving many parties on both sides. Midaz can handle such a scenario in one atomic transaction, crediting and debiting all relevant accounts.

Another example is a peer-to-peer payment with fees: one transaction can debit the payer’s account and credit both the payee’s account and a fee account in one go. This capability simplifies financial flows that would otherwise require multiple steps.

Atomicity and integrity

Transactions are atomic operations – either all constituent operations succeed, or none do. This ensures that partial financial events do not occur. If any part of a transaction fails validation (say, insufficient funds in one account), the entire transaction will not be applied, preserving ledger integrity.


Transaction source


Transactions in Midaz can be made either from a single source or multiple sources.

❗️

Important

The sum of the values in the source must always equal the value specified right after the send and equal the sum of the values in the distribute.


Single source

In a single-source transaction, the specified amount is taken directly from one source account.

Example

In this example (figure 1):

  • BRL 30.00 is taken from an account (@account1).
  • 100% of it is sent to a destination account (@destinationAccount1).
Figure 1. Single source transaction.

Figure 1. Example of a single source transaction.

Code examples

(transaction v1  
  (description "single source transaction")  
  (send BRL 3000|2
    (source
      (from @account1 :amount BRL 3000|2)		
    )
    (distribute
      (to @destinationAccount1 :share 100)
    )	
  )
)
{
   "description":"single source transaction",
   "send":{
      "asset":"BRL",
      "value":"30.00",
      "source":{
         "from":[
            {
               "account":"@account1",
               "amount":{
                  "asset":"BRL",
                  "value":"30.00"
               }
            }
         ]
      },
      "distribute":{
         "to":[
            {
               "account":"@destinationAccount1",
               "share":{
                  "percentage":100
               }
            }
         ]
      }
   }
}

Multi-source

In a multi-source transaction, the amount or proportion is taken from different accounts. The total amount withdrawn from each account must be equal to the transferred amount to avoid any errors.

Example

In this example (figure 2):

  • BRL 30.00 will be sent to the destination account (@destinationAccount1).
    • BRL 15.00 will be taken from one account (@account1).
    • BRL 15.00 will be taken from another account (@account2).
  • The destination account will receive 100% of the amount sent.
Figure 2. Multi-source transaction.

Figure 2. Example of a multi-source transaction.

Code examples

(transaction v1
  (description "multi-source transaction")
  (send BRL 3000|2 
    (source
      (from @account1 :amount BRL 1500|2)
      (from @account2 :amount BRL 1500|2)
    )
    (distribute
      (to @destinationAccount1 :share 100)
    )
  )
)
{
   "description":"multi-source transaction",
   "send":{
      "asset":"BRL",
      "value":"30.00",
      "source":{
         "from":[
            {
               "account":"@account1",
               "amount":{
                  "asset":"BRL",
                  "value":"15.00"
               }
            },
            {
               "account":"@account2",
               "amount":{
                  "asset":"BRL",
                  "value":"15.00"
               }
            }
         ]
      },
      "distribute":[
         {
            "account":"@destinationAccount1",
            "share":{
               "percentage":100
            }
         }
      ]
   }
}

Transaction destination


Similar to origins, destinations can be single or multiple.


Single destination

In a single destination transaction, the amount is sent to only one destination account.

Example

In this example (figure 3):

  • BRL 30.00 is taken from an external account (@external|BRL).
  • 100% is sent to the destination account (@destinationAccount1).
Figure 3. Example of a single destination transaction.

Figure 3. Example of a single destination transaction.

Code examples

(transaction v1
  (description "single destination transaction")  
  (send BRL 3000|2
    (source
      (from @external|BRL :amount BRL 3000|2)		
    )
    (distribute
      (to @destinationAccount1 :share 100)
    )	
  )
)
{
   "description":"single destination transaction",
   "send":{
      "asset":"BRL",
      "value":"3000",
      "source":{
         "from":[
            {
               "account":"@external|BRL",
               "amount":{
                  "asset":"BRL",
                  "value":"30.00"
               }
            }
         ]
      },
      "distribute":{
         "to":[
            {
               "account":"@destinationAccount1",
               "share":{
                  "percentage":100
               }
            }
         ]
      }
   }
}

Multi-destination

In multi-destination transactions, the transaction amount is divided among multiple destination accounts. Values can be distributed by shares, fixed amounts, or remaining balance.

Example

In this example (figure 4):

  • BRL 100 is taken from the source account (@account1).
  • 38% of the amount goes to account 2 (@account2).
  • 50% goes to account 3 (@account3).
  • A fixed BRL 2.00 goes to account 4 (@account4).
  • The remaining amount goes to account 5 (@account5).
Figure 4. Example of a multi-destination transaction.

Figure 4. Example of a multi-destination transaction.

Code example

(transaction v1  
  (description "multi-destination transaction")  
  (send BRL 3000|2
    (source
      (from @account1 :amount BRL 3000|2)		
    )
    (distribute
      (to @account2 :share 38)  
      (to @account3 :share 50)  
      (to @account4 :amount BRL 200|2)  
      (to @account5 :remaining)  
    )	
  )
)
{
   "description":"multi-destination transaction",
   "send":{
      "asset":"BRL",
      "value":"30.00",
      "source":{
         "from":[
            {
               "account":"@account1",
               "amount":{
                  "asset":"BRL",
                  "value":"30.00"
               }
            }
         ]
      },
      "distribute":{
         "to":[
            {
               "account":"@account2",
               "share":{
                  "percentage":38
               }
            },
            {
               "account":"@account3",
               "share":{
                  "percentage":50
               }
            },
            {
               "account":"@account4",
               "amount":{
                  "asset":"BRL",
                  "value":"2.00"
               }
            },
            {
               "account":"@account5",
               "remaining":"remaining"
            }
         ]
      }
   }
}

Multi-source and multi-destination


These complex transactions involve multiple sources and multiple destinations. This is useful for scenarios like a crowdfunding campaign where contributions are pooled and distributed among multiple recipients.

Example

In this example (figure 5):

  • BRL 4,000.00 will be donated and the amount will be taken from four different accounts.
    • 25 % will be taken from account 1 (@account1).
    • 25 % will be taken from account 2 (@account2).
    • 40 % will be taken from account 3 (@account3)
    • 10 % will be taken from account 4 (@account4).
  • The donations will be distributed to four separate donation accounts, and each will receive a 25% share of the total.
Figure 5. Example of a multi-source and multi-destination transaction.

Figure 5. Example of a multi-source and multi-destination transaction.

Code examples

(transaction v1 
  (description "multi-source and multi-destination transaction")  
  (send BRL 400000|2  
    (source  
      (from @account1 :share 25)		  
      (from @account2 :share 25)  
      (from @account3 :share 40)  
      (from @account4 :share 10)  
    )
    (distribute  
      (to @donation1 :share 25)  
      (to @donation2 :share 25)  
      (to @donation3 :share 25)  
      (to @donation4 :share 25)  
    )
  )
)
{
   "description":"multi-source and multi-destination transaction",
   "send":{
      "asset":"BRL",
      "value":"4000.00",
      "source":{
         "from":[
            {
               "account":"@account1",
               "share":{
                  "percentage":25
               }
            },
            {
               "account":"@account2",
               "share":{
                  "percentage":25
               }
            },
            {
               "account":"@account3",
               "share":{
                  "percentage":40
               }
            },
            {
               "account":"@account4",
               "share":{
                  "percentage":10
               }
            }
         ]
      },
      "distribute":{
         "to":[
            {
               "account":"@donation1",
               "share":{
                  "percentage":25
               }
            },
            {
               "account":"@donation2",
               "share":{
                  "percentage":25
               }
            },
            {
               "account":"@donation3",
               "share":{
                  "percentage":25
               }
            },
            {
               "account":"@donation4",
               "share":{
                  "percentage":25
               }
            }
         ]
      }
   }
}

Transaction flow


When a transaction is initiated, the system verifies whether the source account has a sufficient balance of the specified asset. If this condition is met and the transaction is not marked as pending (Two-Phase Transaction flow), the specified amount is transferred immediately from the source account to the destination account, using the available balance.

This process is synchronous, and upon success, the transaction status will be APPROVED.

❗️

Important

Users should only initiate this type of transaction if they intend to commit it to the ledger immediately.

However, for transactions that require validation or approval before execution, you can use the pending flag to create a Two-Phase Transaction.

Two-Phase Transaction

In this scenario:

  • The transaction is initially stored with status PENDING.
  • The specified funds are reserved by moving the amount from available to on_hold in the source account(s).
  • No operations (debits or credits) are yet recorded in the ledger.
  • You must explicitly commit to execute the transfer, or cancel to release the funds.

Use cases

Pre-Transactions are ideal when external validation is needed before transferring funds. Common scenarios include:

  • Anti-fraud workflows: Reserve funds before triggering expensive fraud checks. If approved, commit. If rejected, cancel to avoid unnecessary costs.

Figure 7. Anti-fraud workflow example


  • Corporate approvals: Hold funds while waiting for internal approval (e.g., finance, treasury).
  • Scheduled payments: Guarantee execution by reserving the required amount at scheduling time.
  • High-value transfers: Add extra verification steps (e.g., 2FA) before completing the transaction.
👍

Tip

The Two-Phase Transaction feature plays a key role in the workflow plugin, allowing you to reserve funds at the beginning of a workflow and perform validations later—with guaranteed execution if approved.


Two-Phase Transaction flow

  1. Create a Two-Phase Transaction
    1. Use the Create a Transaction using JSON endpoint with "pending": true.

In this validation step, Midaz will:

  • Validate accounts and their transaction permissions (allowSending, allowReceiving).
  • Ensure sufficient available balance.
  • Validate send, source, and distribute structure.

If valid:

  • Reserve funds by moving them to on_hold.
  • Set transaction status to PENDING.
  • Store transaction metadata (no ledger operations yet).
  1. Commit or cancel the pending transaction
  • Commit

    Finalizes the transaction. Transfers funds from on_hold to the destination account and records debit/credit operations. Status: APPROVED.

  • Cancel

    Releases reserved funds back to available without executing the transfer. Status: CANCELED.


Real-time event publishing


Want to track the status of your transactions as they happen? Midaz supports real-time event publishing via RabbitMQ.

Once enabled, every transaction generates an event, such as APPROVED, PENDING, or CANCELED, that external systems can subscribe to using topic-based routing.

For more information about how to publish and consume transaction events, check out the Event publisher page.


Inflows, outflows, and external accounts


Midaz uses a double-entry ledger system in which all value entering or leaving the system must pass through a special account: the External Account. This account—represented as @external/{{assetCode}}—acts as the bridge between Midaz and the external financial world (banks, PSPs, payment rails, etc.).

Why this matters

When the ledger is first initialized, all accounts—including @external—start with a zero balance. To reflect real-world balances (e.g., institutional funds held externally), you must initiate a transaction that injects funds into Midaz accounts by debiting the external account.

This is the only way to bring funds into Midaz.

Inflows – Adding value into the Ledger

To credit an internal account from outside the ledger:

  • Source: @external/{{assetCode}} (e.g., @external/BRL).
  • Destination: One or more internal accounts (e.g., @organization.main).

Example: First deposit into the Ledger

Your institution holds R$10,000 in a real-world bank and wants to bring it into Midaz.

You create a transaction:

SourceDestinationAmount
@external/BRL@accountABRL 10,000

This debits the external account and credits your internal account. The external account will now show a negative balance, which is expected and represents the total amount your organization has brought into the ledger.

Outflows – Moving value out of the Ledger

To simulate value leaving the ledger to an external destination:

  • Source: One or more Midaz accounts.
  • Destination: @external/{{assetCode}}.

Example: A PIX transfer from Ledger to an external bank

SourceDestinationAmount
@accountA@external/BRLBRL 1,000

This debits @accountA and credits the external account. In real life, your system (via SPI or other integrations) will then transfer the funds to the intended recipient.

Behavior and balance rules

  • @external/{{assetCode}} can have a zero or negative balance, but never positive.
  • Its balance is always the inverse of the combined balance of all Midaz accounts holding that asset.
  • Every inflow increases internal liquidity and reduces the external account balance (i.e., simulates a deposit).
  • Every outflow does the reverse.
📘

Note

All value movements between the outside world and the Midaz ledger must go through the external account.

Nothing enters or exits the system without a formal transaction—ensuring full traceability, balance integrity, and compliance with double-entry principles.


Initiating a transaction


There are two main ways to initiate a transaction:

Using DSL

A Domain-Specific Language (DSL) simplifies user interaction by focusing on specific domain concepts, enabling non-developers to perform complex tasks without technical skills. DSLs reduce boilerplate code and embed constraints in their syntax, enforcing business rules and minimizing errors. However, their predefined patterns can limit flexibility, making it harder to create custom parsers or adapt to new requirements.

The Transactions DSL in Midaz, called Gold, simplifies transaction processing with an intuitive accounting syntax. It stores transaction information in .gold files, allowing business teams to define transactions easily and fostering collaboration between technical and business workflows.

To use the DSL, follow these steps:

  1. Create the .gold file according to the Transactions DSL structure.
  2. Submit the file using the Create a Transaction using DSL endpoint.
👍

Tip

Want to dive deeper into the structure of the DSL? Check out the Transactions DSL page for more details..


Using JSON endpoint

JSON endpoints provide a flexible and developer-friendly standard for data interchange, enabling precise control over request structures tailored for custom workflows and specific use cases. Their broad compatibility with various programming languages makes integration and debugging easier.

However, this flexibility can result in verbosity and user errors, as developers are required to handle validation manually. For non-developers, the complexity of JSON may present challenges, making it less intuitive than a Domain-Specific Language (DSL) for everyday tasks.

❗️

Important

If you need to reserve funds before completing the transfer, set the pending field to true (Two-Phase Transaction flow).


Transaction Routes


The Transaction Routes API enables structured and validated transaction processing in Midaz.

While the Transactions API handles the execution of financial events (debits and credits between accounts), Transaction Routes define templates for how these events should be structured and validated, ensuring consistency and integrity.

Think of it as the validation layer that ensures business transactions follow predefined patterns and maintain proper financial structure.

For example, a transaction for a fee, a deposit, or a payout may require different account types, validation rules, and structures. Instead of handling validation separately for each transaction, you configure predefined rules that tell Midaz: "When the user submits this type of transaction, validate it against these account requirements and structure patterns."

Each Transaction Route combines multiple Operation Routes, which define the individual components of a transaction, specifying account requirements, directions (source/destination), and validation rules for each "leg" of the financial event.

Why it matters

By using Transaction Routes, you:

  • Ensure consistent transaction structure across your application.
  • Make your ledger more maintainable, predictable, and reliable.
  • Validate financial events against predefined patterns.
  • Enable configuration of transaction templates without code changes.
  • Maintain data integrity through structured validation.

Managing transactions


You can manage your Transactions either via API or through the Console.

Via API


Via Console

All Transaction management actions, including viewing, creating, editing, and deleting, can be done through the Organizations page in the Midaz Console.

Learn more in the Managing Transactions guide.