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

> Follow Fees Engine best practices for naming packages, setting fee priorities, segmenting customers, and running production setups with full auditability.

Fees Engine controls how you calculate, apply, and track fees. Production use demands careful configuration and operational discipline, because accuracy and auditability are non-negotiable.

These recommendations complement the [Fees Engine overview](/en/midaz/fees/fees-engine-overview) and the [calculation mechanics](/en/midaz/fees/fee-engine-calculation) guide.

## 1. Design fee packages with clear naming and segmentation

***

Fee packages are the foundation of your fee logic. A well-organized package structure makes it easier to maintain, debug, and audit your fee configuration over time.

* **Use descriptive names** that reflect the business context (e.g., "pix-transfer-standard", "wire-premium-segment").
* **Segment by product and customer group** using `segmentId`. This allows you to apply different fee rules to different customer tiers without creating conflicting packages.
* **Keep packages focused**. A package that tries to cover too many scenarios becomes hard to test and maintain. Prefer multiple focused packages over one that does everything.
* **Document your package structure** internally. As your package count grows, a clear reference of which package applies where prevents misconfiguration.

## 2. Set fee priorities carefully

***

When a package contains multiple fees, the `priority` field determines the order of execution. Getting this wrong can produce incorrect calculations.

* **Priority 1 must always use `referenceAmount: originalAmount`**. The engine enforces this.
* **Fees with `isDeductibleFrom: true` must also use `referenceAmount: originalAmount`**. Deductible fees then always apply to the full transaction value.
* **Priority must be unique within a package**. The engine rejects duplicate priorities.
* **Think about fee dependencies**. If one fee adjusts the transaction value and another fee should be calculated on the adjusted value, use `referenceAmount: afterFeesAmount` with a higher priority number. If the second fee should reference the original value, use `originalAmount`.

<Tip>
  When in doubt, start with a simple configuration (one or two fees per package) and validate the results using the estimate endpoint before adding complexity.
</Tip>

## 3. Always estimate before applying fees in production

***

Fees Engine provides an [estimate endpoint](/en/reference/midaz/plugins/fees-engine/simulate-fees) that lets you preview fee calculations without writing anything to the ledger.

Use estimation to:

* **Validate new packages** before activating them. Confirm that the calculated values match your expected results across different transaction amounts.
* **Test edge cases**: zero-amount transactions, boundary values at `minimumAmount` and `maximumAmount` thresholds, and exempted accounts.
* **Preview fees for users**. If your product shows fees before confirmation, use the estimate endpoint to provide accurate previews.
* **Debug unexpected results**. If a calculated fee doesn't match expectations, estimate the same transaction with a specific `packageId` to isolate the issue.

<Note>
  The [calculate endpoint](/en/reference/midaz/plugins/fees-engine/calculate-fees) automatically selects the best matching package. The estimate endpoint requires a specific `packageId`, giving you full control over which package to test.
</Note>

## 4. Manage exemptions explicitly

***

Fees Engine supports two types of exemptions: by **transaction amount range** and by **account**.

* **Amount ranges** (`minimumAmount`, `maximumAmount`): Define the transaction value window in which fees apply. Transactions outside this range are exempt. Use this for promotional thresholds or tiered pricing.
* **Waived accounts** (`waivedAccounts`): Specific accounts exempt from fees within a package. Use this for internal accounts, employee accounts, or partnership arrangements.

Best practices for exemptions:

* **Keep waived account lists short and reviewed**. Large lists become hard to audit. Periodically review which accounts are exempted and why.
* **Document the business reason** for each exemption in your internal records.
* **Test exemption boundaries**. If your range is R$ 0–300, make sure transactions at exactly R$ 300 and R\$ 301 behave as expected.

## 5. Enable and tune caching for performance

***

Fees Engine caches fee packages in memory to reduce database queries during high traffic.

Configure caching through environment variables:

```yaml theme={null}
fees:
  configmap:
    PACKAGE_CACHE_ENABLED: "true"
    PACKAGE_CACHE_TTL_SECONDS: "600"
```

* **`PACKAGE_CACHE_ENABLED`** (default: `true`): Enables or disables the package cache.
* **`PACKAGE_CACHE_TTL_SECONDS`** (default: `180`): Time-to-live in seconds before the engine refreshes cached packages from the database.

Recommendations:

* **Keep caching enabled in production**. It significantly reduces latency for high-volume transaction processing.
* **Adjust TTL based on your change frequency**. If you update packages frequently, use a shorter TTL (e.g., 60–120 seconds). If packages are stable, the default 180 seconds is appropriate.
* **Be aware of cache delay**. After you update a package, the change may take up to the configured TTL to propagate. If you need immediate effect, restart the service or temporarily reduce the TTL.

## 6. Use correct numeric values

***

Express all financial values in Fees Engine as **strings** using the `numeric` type. This prevents floating-point precision errors that are common with decimal arithmetic.

```json theme={null}
"value": "12.50"
```

* Always send values as strings, even whole numbers (e.g., `"100"` not `100`).
* Never use floating-point types for monetary calculations in your integration layer.
* Note that the engine automatically adjusts fee splits with repeating decimals (e.g., R\$ 10 divided by 3 accounts) to keep ledger totals exact.

<Warning>
  Fees Engine requires **Midaz v3.x.x** or later. The v2.x.x `amount` + `scale` format is not compatible. Upgrade Midaz before deploying Fees Engine.
</Warning>

## 7. Use soft delete for auditability

***

When you delete a fee package, Fees Engine marks it with a `deletedAt` timestamp rather than removing it from the database. This preserves the audit trail for historical transactions that referenced that package.

* **Don't rely on hard deletion** for fee packages in production. Historical transactions may reference deleted packages for reconciliation.
* **Periodically review** deleted packages if your database grows significantly. Archiving strategies can help manage storage without losing audit capability.

## 8. Monitor the Fees Engine in production

***

Fees Engine supports OpenTelemetry for traces and metrics. Enable it to gain visibility into fee calculation performance and behavior.

```yaml theme={null}
fees:
  configmap:
    ENABLE_TELEMETRY: "true"
    OTEL_RESOURCE_SERVICE_NAME: "plugin-fees"
```

In production:

* **Monitor health endpoints**. Fees Engine exposes `/health` for readiness and liveness checks (default port: 4002).
* **Set up alerts** for sustained high latency on fee calculations, which may indicate database contention or cache misconfiguration.
* **Monitor MongoDB** connection pool usage, disk space, and replication health. The default `MONGO_MAX_POOL_SIZE` is 100.
* **Review pod resource usage** against your traffic patterns and autoscaling behavior.

## 9. Keep versions compatible

***

Before upgrading Fees Engine:

* Check the [version compatibility table](/en/platform/plugins/midaz-version-compatibility) to confirm compatibility with your Midaz Core version.
* Always upgrade **Midaz Core first**, then Fees Engine.
* Back up your MongoDB data and Helm values before any major upgrade.
* Test the upgrade in a staging environment before applying it to production.

For upgrade procedures, see the [Helm upgrade guide](/en/platform/helm/midaz/midaz-upgrade-guide).

## 10. Review the security recommendations

***

Fees Engine processes financial data and integrates with Midaz ledger operations. Make sure your deployment follows the platform-wide [Security recommendations](/en/midaz/security-recommendations), which cover:

* Network segmentation and Zero Trust Architecture
* Secret management and rotation (including `LICENSE_KEY` and database credentials)
* TLS 1.2+ enforcement for all communications
* RBAC configuration via [Access Manager](/en/platform/access-manager/access-manager)
* Patch management and vulnerability scanning

## 11. Design billing packages with clear scope

***

Each billing package should represent a single, well-defined charge. Avoid packing unrelated pricing into one package.

* **Separate packages per transaction route**. A package for Pix billing and a package for boleto billing are clearer than one package that tries to handle both.
* **Use descriptive labels** that include the billing type and target: "Pix Send Monthly Billing — Standard Tier" is better than "Billing Package 1".
* **One `accountTarget` type per maintenance package**. You cannot combine `segmentId`, `portfolioId`, and `aliases` in the same package. If you need different targets, create separate packages — a single `/billing/calculate` call evaluates all active packages.

## 12. Write commercial terms against route volume

***

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.

* **State thresholds as route volume.** "The first 100 transactions on the `pix-send` route are free" maps directly onto a package. Write the contract in the same terms the engine bills in.
* **Split routes to split counts.** One package per transaction route keeps each flow on its own quota, tiers, and discounts.

## 13. Plan free quotas and discount tiers carefully

***

Fees Engine evaluates free quotas and discounts in a specific order:

1. The engine subtracts the free quota from the total count to get the billable count.
2. The engine prices the billable count — for tiered packages it charges every billable unit at the single matching tier's rate (volume pricing, not graduated).
3. The engine applies one discount tier to the gross amount, chosen against the **total** count (before the free quota was subtracted).

Design considerations:

* **Free quotas reset each billing period**. Set the value based on your commercial agreement per period (monthly, weekly, or daily), not lifetime.
* **Tiers are volume brackets, not slices**. A billable count of 1,750 against a 501–2,000 tier prices all 1,750 units at that tier's rate. Crossing a bracket boundary changes the rate for the whole volume, so tier boundaries can move the bill sharply.
* **Cover every positive billable count.** If the billable count is positive and no tier's range contains it, the calculation fails for that package. Leave the top tier's upper bound open. Start the first tier at 1 — a billable count of zero produces a zero amount and an empty payload without a tier lookup.
* **Discount tiers are cumulative thresholds**, not ranges, and only one applies: the highest `minQuantity` the total count reaches. If you define discounts at 200 and 400 transactions, a client with 500 transactions gets the 400+ discount — not both.
* **Watch the two different counts.** Pricing uses the billable count (after free quota); the discount uses the total count (before it).

<Tip>
  Use test calculations with known transaction counts to validate your tier and discount configuration before enabling the package in production.
</Tip>

## 14. Size maintenance account targets appropriately

***

Maintenance packages support three target types with different scale profiles:

| Target type   | Scale                 | Use case                                         |
| ------------- | --------------------- | ------------------------------------------------ |
| `segmentId`   | 100,000+ accounts     | Standard tiers (PF, PJ, premium)                 |
| `portfolioId` | Thousands of accounts | Business portfolios (PME, Corporate, Enterprise) |
| `aliases`     | Up to 100 accounts    | Specific named accounts                          |

Choose the target type that matches your operational scale. If you find yourself listing hundreds of aliases, migrate to a segment or portfolio in Midaz instead.

## 15. Handle billing failures with re-execution

***

Billing calculation follows an all-or-nothing policy. If any package fails, the engine returns no results.

* **Build retry logic** into your orchestrator. The calculation is stateless — re-executing for the same period produces the same results.
* **Check error responses** for the specific package and resource that failed. Common causes: invalid `ledgerId`, unreachable Midaz API, or disabled billing packages.
* **Separate volume and maintenance calculations** if one type consistently succeeds while the other fails. Call `/billing/calculate` with `"type": "volume"` and `"type": "maintenance"` independently to isolate failures.
