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

# Fees Engine calculations

> Learn how the Fees Engine calculates fees, handles decimal precision, splits amounts across accounts, and applies exemptions over daily or monthly periods.

## Numeric values (string)

***

Express all financial values in **Fees Engine** as a `string` with the `numeric` type. This gives high-precision decimal handling for assets like BRL or BTC. It also prevents rounding errors during calculations, splits, or exemptions.

<Danger>
  **Important**

  * **Required**: Midaz v3.x.x (uses `numeric`).
  * **Incompatible**: Midaz v2.x.x (deprecated `amount` + `scale` format).

  Clients using Midaz v2.x.x must upgrade to v3.x.x to ensure proper integration and functionality with Fees Engine.
</Danger>

**Example:**

```
"value": "12.50"
```

## Billing periods

***

When triggering a billing calculation, you specify the time window through the `period` field. Fees Engine supports three formats:

| Format  | Example      | Window                                                             |
| ------- | ------------ | ------------------------------------------------------------------ |
| Daily   | `2026-03-15` | Start of that day → start of the next day (UTC)                    |
| Weekly  | `2026-W13`   | Monday 00:00 UTC of the ISO week → following Monday 00:00 UTC      |
| Monthly | `2026-03`    | First instant of the month → first instant of the next month (UTC) |

The engine uses the period to count qualifying transactions (for volume packages) or active accounts (for maintenance packages) within that exact window.

<Tip>
  Weekly periods follow [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Week_dates). Week numbering ranges from `W01` to `W52` (or `W53` in years that have 53 ISO weeks). The week always starts on Monday.
</Tip>

Choose the granularity that matches your billing cycle. A prepaid card product billed daily would use `2026-03-15`. A SaaS platform billed monthly would use `2026-03`. A marketplace that settles weekly would use `2026-W13`.

## Fee calculation rules

***

Each fee uses an `applicationRule` to define how it's calculated. You can choose from three rule types:

* [maxBetweenTypes](#maxbetweentypes)
* [flatFee](#flatfee)
* [percentual](#percentual)

You can combine different rules in a single package to match your use case.

Other key fields:

* `isDeductibleFrom`: defines if the fee is deducted from the sender or the receiver.
* `referenceAmount`: either `originalAmount` or `afterFeesAmount`.
* `priority`: defines the order of application. Priority 1 must always use `originalAmount`.

### maxBetweenTypes

Applies whichever is greater: a flat or percentage-based fee.

**Example**

* Flat fee value: R\$5.
* Percentual fee: 2%.
* Reference amount: R\$1,000.

```
rate = 1000 * 0.02 = R$ 20.00
```

Since **R\$ 20 > R\$ 5**, the engine applies the percentage-based fee.

### flatFee

Applies a fixed fee amount. Behavior depends on `isDeductibleFrom`.

**Example**

* Flat fee: R\$15.
* Reference amount: R\$115.

| `isDeductibleFrom` | Formula                 | Total Fee  |
| :----------------- | :---------------------- | :--------- |
| `false`            | `referenceAmount + fee` | R\$ 130.00 |
| `true`             | `referenceAmount - fee` | R\$ 100.00 |

### percentual

Applies a fee as a percentage of the reference amount.

**Example**

* Value: 30%.
* Reference amount: R\$ 389.50.

| `isDeductibleFrom` | Formula                                       | Total Fee  |
| :----------------- | :-------------------------------------------- | :--------- |
| `false`            | `referenceAmount * value`                     | R\$ 116.85 |
| `true`             | `referenceAmount - (referenceAmount * value)` | R\$ 272.65 |

## Fee splitting

***

When a transaction has multiple source accounts, Fees Engine splits fees proportionally.

### Example

* Total amount: R\$4,000.00
* Fixed fee: R\$15.00
* Tax: 4%
* `isDeductibleFrom: false`

### Participation %

> **Formula**: (Account Amount ÷ Total Amount) × 100

| Account   | Share | Amount    |
| :-------- | :---- | :-------- |
| @account1 | 25%   | R\$ 1,000 |
| @account2 | 25%   | R\$ 1,000 |
| @account3 | 40%   | R\$ 1,600 |
| @account4 | 10%   | R\$ 400   |

### Fixed fee distribution

> **Formula**: `fixed Fee × participation %`

| Account   | Fee Share | Total        |
| :-------- | :-------- | :----------- |
| @account1 | R\$ 3.75  | R\$ 1,003.75 |
| @account2 | R\$ 3.75  | R\$ 1,003.75 |
| @account3 | R\$ 6.00  | R\$ 1,606.00 |
| @account4 | R\$ 1.50  | R\$ 401.50   |

### Proportional tax

> **Formula**: `account amount × tax %`

| Account   | Tax       | Total w/ Tax |
| :-------- | :-------- | :----------- |
| @account1 | R\$ 40.00 | R\$ 1,040.00 |
| @account2 | R\$ 40.00 | R\$ 1,040.00 |
| @account3 | R\$ 64.00 | R\$ 1,664.00 |
| @account4 | R\$ 16.00 | R\$ 416.00   |

### Final amount per account

> **Formula**: `principal + fee + tax`

| Account   | Fee      | Tax       | Final Total  |
| :-------- | :------- | :-------- | :----------- |
| @account1 | R\$ 3.75 | R\$ 40.00 | R\$ 1,043.75 |
| @account2 | R\$ 3.75 | R\$ 40.00 | R\$ 1,043.75 |
| @account3 | R\$ 6.00 | R\$ 64.00 | R\$ 1,670.00 |
| @account4 | R\$ 1.50 | R\$ 16.00 | R\$ 417.50   |

#### Validations

* Total shares = 100%
* Fee split matches flat fee
* Tax split = 4%
* Total sent = R\$ 4,175.00

## Fee exemptions: rules & hierarchy

***

### By transaction amount

Use `minimumAmount` and `maximumAmount` to define when **fees should apply**.

> For example: If the range is R$ 0–300, a transaction of R$ 301 won’t trigger fees.

### By account

The system checks `waivedAccounts`. A source in that list is exempt from fees.

<Tip>
  **Hierarchy:** Value range check > then account exemption
</Tip>

## Mixed example: fee exemptions and proportional fee splitting

***

Let’s look at an example of a package that includes accounts with fee exemptions and requires splitting fees proportionally.

### Scenario

We’re processing a transaction of **R\$ 4,000**, which includes:

* A **fixed fee** of **R\$ 16**.
* Only some accounts are subject to the fixed fee
* An **IOF tax** of **6%** to be deducted.

#### Split on the source side

| Source Account | %   | Proportional Value |
| :------------- | --- | :----------------- |
| @account1      | 15% | R\$ 600            |
| @account2      | 35% | R\$ 1,400          |
| @account3      | 40% | R\$ 1,600          |
| @account4      | 10% | R\$ 400            |

The engine applies the fixed fee only to `@account3` and `@account4`.

#### Result after Admin Fee (proportional)

| Account   | Admin Fee | Total        |
| :-------- | :-------- | :----------- |
| @account1 | Exempt    | R\$ 600      |
| @account2 | Exempt    | R\$ 1,400    |
| @account3 | R\$ 12.80 | R\$ 1,612.80 |
| @account4 | R\$ 3.20  | R\$ 403.20   |

Total send value increases to **R\$4,016**.

#### IOF deduction (recipient)

| Recipient  | %   | Gross     | IOF (6%) | Net     |
| :--------- | --- | :-------- | :------- | :------ |
| @donation1 | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation2 | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation3 | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |
| @donation4 | 25% | R\$ 1,000 | R\$ 60   | R\$ 940 |

<Danger>
  The engine credits fees to the accounts defined in each fee’s `creditAccount`.
</Danger>

## Repeating decimals

***

When a fee split produces a repeating decimal (for example, 0.3333...), Fees Engine keeps every fee leg at full precision. It reconciles the small remainder onto the fee for the account with the largest value. This keeps the total exact, avoids rounding drift, and keeps your ledger consistent.

## Billing calculations

***

Billing packages use a different calculation model than fee packages. Instead of evaluating individual transactions, they aggregate data over a billing period and return charge payloads for your orchestrator to execute.

Billing supports three period formats: monthly (`YYYY-MM`), weekly (`YYYY-Www`, e.g., `2026-W13`), and daily (`YYYY-MM-DD`).

### Volume billing calculation

Volume billing counts transactions matching an `eventFilter` (transaction route + status) within the billing period, then applies pricing based on the configured model.

The calculation follows this order:

1. **Count** qualifying transactions in the period.
2. **Subtract** the `freeQuota` from the total count to get the billable count.
3. **Apply pricing** based on the `pricingModel` (`tiered` or `fixed`).
4. **Apply a discount** from `discountTiers`, evaluated against the **total** count (before the free quota was subtracted).

Amounts are kept at full decimal precision throughout — the engine applies no asset-scale rounding.

#### Tiered pricing

Tiered pricing is **volume pricing, not graduated pricing**. The engine finds the single tier whose quantity range contains the billable count, then charges **every** billable unit at that tier's unit price. It does not price each unit inside its own range.

A tier's range is inclusive on both ends. If you omit the upper bound, the tier is unbounded.

The engine looks up a tier only when the billable count is positive. If the billable count is positive and no tier covers it, the calculation fails for that package. A billable count of zero skips the tier lookup and produces a zero amount, so your tiers do not need to cover zero.

**Example**: A billing package for boleto issuance with three tiers and a free quota of 50:

| Range     | Unit price |
| --------- | ---------- |
| 1–500     | R\$ 1.20   |
| 501–2,000 | R\$ 0.80   |
| 2,001+    | R\$ 0.45   |

For a client that issued 1,800 boletos in the month:

* 50 exempt (free quota) → 1,750 billable
* 1,750 falls in the 501–2,000 tier, so **all** 1,750 units are priced at R\$ 0.80: R\$ 1,400.00 gross
* Discount tier applies on the total count of 1,800 (≥ 1,000 → 5%): −R\$ 70.00
* **Net total: R\$ 1,330.00**

#### Fixed pricing

A single unit price applies to all billable transactions regardless of volume. The engine still subtracts the free quota before calculation. Fixed pricing takes its unit price from the **first tier** in the package, so a fixed package must still declare at least one tier — otherwise the calculation fails.

**Example**: R\$ 0.10 per Pix sent, no free quota:

* 5,000 Pix transactions × R\$ 0.10 = R\$ 500.00

#### Discount tiers

At most **one** discount tier applies: the one with the largest `minQuantity` that the total count meets or exceeds. Its percentage is applied to the gross amount. Discounts are evaluated against the total transaction count, not the billable count.

#### Count scope

Volume calculation counts transactions **per transaction route across the whole ledger**. The free quota, the tiers, and the discount tiers all apply to that route-level total. To count two flows separately, create one package per transaction route.

### Maintenance billing calculation

Maintenance billing charges a fixed amount per active account in the billing period. The engine resolves the target accounts, keeps only the active ones, and generates a single transaction payload.

The result is a N:1 transaction:

* Each active account appears as a debit entry (`source.from`) for the configured `feeAmount`.
* The `maintenanceCreditAccount` receives the full total as a single credit entry (`distribute.to`).

Only accounts whose status code is `active` are included; every other status is excluded. If no account resolves, the package returns an empty payload (`{}`) instead of a transaction.

**Example**: Monthly maintenance of R\$ 9.90 for a segment with 12,000 active PF accounts:

* 12,000 entries in `source.from`, each debited R\$ 9.90
* 1 entry in `distribute.to` credited R\$ 118,800.00

### Zero-amount results

When a package's net amount comes out zero — for example the free quota covered every transaction — the engine still returns a result for that package, but with an empty transaction payload (`{}`). Treat that as "processed, nothing to submit".

Fully exempt usage reaches this result without a tier lookup. The free quota brings the billable count to zero, the engine skips tier matching, and the amount is zero. A package whose tiers start at 1 is correct for this case.

### All-or-nothing failure policy

If any billing package fails during a `/billing/calculate` call, the entire operation fails. The engine returns no partial results. The response includes which package and resource caused the failure, so you can fix and re-execute.

### Audit metadata

Each billing calculation result carries structured metadata for traceability. Volume results include the billing type, package id and label, period, total and billable event counts, free quota used, pricing model, gross and net amounts, and the discount detail (percentage, amount, and `minQuantity`) when one applied. Maintenance results include the billing type, package id and label, period, total account count, and the per-account fee amount.
