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

# Fee rules

> Configure fee schedules and predicate rules used by NET or GROSS normalization when the matching side and currencies are compatible.

Fee handling in Matcher is split across two entities that work together:

* A **fee schedule** defines *how much* fee to compute — the currency, rounding, and one or more fee items (flat, percentage, tiered, or expression-based).
* A **fee rule** decides *when* a fee schedule applies. It belongs to a reconciliation context, targets a matching side, and carries predicates that a transaction's metadata must satisfy. When the predicates match, the rule selects its fee schedule.

When fee normalization is enabled and the transaction and schedule currencies match, modeling expected fees this way lets Matcher account for predictable charges before comparing transactions.

## What fee handling solves

***

Reconciliation fails when one side of a transaction includes fees or charges that the other side doesn't record. A payment gateway deducts a processing fee before settling. A bank charges a wire transfer fee. An acquirer nets fees against payouts.

Without modeled fees, Matcher treats these differences as amount mismatches and generates exceptions — even when the difference is expected and documented. Fee schedules describe the charge, and fee rules select when to use it during enabled fee normalization.

## How rules and schedules fit together

***

A fee rule does not contain the fee amount or calculation itself. It **references** a fee schedule by ID and applies it to the transactions its predicates select.

1. A **fee schedule** is created once at the tenant level and can be reused by many rules across many contexts.
2. A **fee rule** is created inside a context. It sets a `side`, a `feeScheduleId`, a `priority`, and a list of `predicates`.
3. When Matcher processes a context with fee normalization enabled, it evaluates the relevant side's fee rules in `priority` order (lowest first). The first matching rule selects the referenced schedule.

This separation means you change *how* a fee is computed by editing the schedule, and change *when* it applies by editing the rule — without touching the other.

<Note>
  A fee rule alone does not change comparison amounts. Fee-rule evaluation affects fee reconciliation only when the context sets `feeNormalization` to `NET` or `GROSS`, the relevant side has rules, and the transaction and schedule currencies match.
</Note>

## Fee rule structure

***

A fee rule belongs to a context and has the following fields.

| Field           | Type    | Description                                                                                                                                                |
| --------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `side`          | Enum    | Which matching side the rule applies to: `LEFT`, `RIGHT`, or `ANY`. `ANY` matches transactions on either side.                                             |
| `feeScheduleId` | UUID    | The fee schedule this rule applies when its predicates match.                                                                                              |
| `name`          | String  | Human-readable name of the fee rule.                                                                                                                       |
| `priority`      | Integer | Evaluation priority; lower numbers are evaluated first. Must be unique within the context. `LEFT`, `RIGHT`, and `ANY` rules share the same priority space. |
| `predicates`    | Array   | Predicates (ANDed together) that a transaction's metadata must satisfy for this rule to apply.                                                             |

### Predicates

Each predicate tests one transaction metadata field with an operator.

| Field      | Description                                                                    |
| ---------- | ------------------------------------------------------------------------------ |
| `field`    | The transaction metadata field the predicate tests (e.g. `institution`).       |
| `operator` | The comparison operator (see below).                                           |
| `value`    | Single comparison value, used by `EQUALS`, `NEQ`, and the numeric comparators. |
| `values`   | List of candidate values, used by `IN` and `BETWEEN`.                          |

**Available operators:**

| Operator                    | Meaning                                                                                                                    |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `EQUALS`                    | Case-insensitive string equality with a single `value`.                                                                    |
| `NEQ`                       | For a present field, numeric inequality when both values coerce to decimals; otherwise case-insensitive string inequality. |
| `IN`                        | Matches any entry in `values`.                                                                                             |
| `EXISTS`                    | Asserts the field is present (no value needed).                                                                            |
| `GT` / `GTE` / `LT` / `LTE` | Numeric comparison of the field against a single decimal `value`.                                                          |
| `BETWEEN`                   | Inclusive numeric membership in `values` = `[lo, hi]` (with `lo <= hi`).                                                   |

Missing fields evaluate to `false` for every operator except `EXISTS`.

## Fee schedule structure

***

A fee schedule is a tenant-level entity that computes a fee from a gross amount.

| Field              | Type    | Description                                                                                                                                    |
| ------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`             | String  | Human-readable name of the schedule.                                                                                                           |
| `currency`         | String  | ISO 4217 currency the schedule's amounts are denominated in.                                                                                   |
| `applicationOrder` | Enum    | How items combine: `PARALLEL` applies every item to the same gross base; `CASCADING` applies each item to the net remaining after prior items. |
| `roundingScale`    | Integer | Number of decimal places fee amounts are rounded to.                                                                                           |
| `roundingMode`     | Enum    | Rounding strategy: `HALF_UP`, `BANKERS`, `FLOOR`, `CEIL`, or `TRUNCATE`.                                                                       |
| `items`            | Array   | One or more fee items that make up the schedule (at least one required).                                                                       |

### Fee items

Each item declares a `name`, a `priority` (application order, relevant for `CASCADING`), a `structureType`, and a type-specific `structure`.

| `structureType` | `structure` shape                                          | Notes                                                                                                                                                                                                                                                             |
| --------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FLAT`          | `{ "amount": "1.50" }`                                     | Fixed amount.                                                                                                                                                                                                                                                     |
| `PERCENTAGE`    | `{ "rate": "0.029" }`                                      | `rate` is a `0..1` fraction of the base (`0.029` = 2.9%), **not** a percent value.                                                                                                                                                                                |
| `TIERED`        | `{ "tiers": [ { "rate": "0.01", "upTo": "1000" }, ... ] }` | Same `0..1` fraction semantics per tier rate.                                                                                                                                                                                                                     |
| `EXPRESSION`    | `{ "expression": "gross - desconto + multa" }`             | Formula over identifiers (`+ - * /`, parentheses, and the functions `days_late`, `days_between`, `max`, `min`, `abs`, `clamp`). `gross` is the engine-provided base amount and overrides any metadata key named `gross`; other identifiers resolve from metadata. |

## Managing fee schedules

***

Fee schedules are tenant-scoped and managed independently of any context.

| Operation                  | Endpoint                                       |
| -------------------------- | ---------------------------------------------- |
| Create a fee schedule      | `POST /v1/fee-schedules`                       |
| List fee schedules         | `GET /v1/fee-schedules`                        |
| Retrieve a fee schedule    | `GET /v1/fee-schedules/{scheduleId}`           |
| Update a fee schedule      | `PATCH /v1/fee-schedules/{scheduleId}`         |
| Delete a fee schedule      | `DELETE /v1/fee-schedules/{scheduleId}`        |
| Simulate a fee calculation | `POST /v1/fee-schedules/{scheduleId}/simulate` |

### Create a fee schedule

This schedule computes a 2.9% processing fee on the gross amount, denominated in BRL.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/fee-schedules" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "name": "Gateway Processing - 2.9%",
   "currency": "BRL",
   "applicationOrder": "PARALLEL",
   "roundingScale": 2,
   "roundingMode": "HALF_UP",
   "items": [
     {
       "name": "processing",
       "priority": 1,
       "structureType": "PERCENTAGE",
       "structure": { "rate": "0.029" }
     }
   ]
 }'
```

<Tip>
  API Reference:

  * [Create fee schedule](/en/reference/matcher/create-fee-schedule)
  * [List fee schedules](/en/reference/matcher/list-fee-schedules)
  * [Retrieve fee schedule](/en/reference/matcher/retrieve-fee-schedule)
  * [Update fee schedule](/en/reference/matcher/update-fee-schedule)
  * [Delete fee schedule](/en/reference/matcher/delete-fee-schedule)
</Tip>

### Simulate a fee schedule

Before wiring a schedule into a rule, simulate it against a gross amount to confirm the computed fee.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/fee-schedules/{scheduleId}/simulate" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "grossAmount": "100.00",
   "currency": "BRL"
 }'
```

The response returns the `netAmount`, `totalFee`, and a per-item breakdown.

<Tip>API Reference: [Simulate fee calculation](/en/reference/matcher/simulate-fee-schedule)</Tip>

<Note>
  A fee schedule cannot be deleted while a fee rule or fee-variance history references it. The delete request returns `409 Conflict` with the code `MTCH-0108`; the API problem does not list the blocking contexts. Remove or repoint current rule references first. Historical variance references continue to block deletion.
</Note>

## Managing fee rules

***

Fee rules are created inside a context and reference a fee schedule.

| Operation                    | Endpoint                                  |
| ---------------------------- | ----------------------------------------- |
| Create a fee rule            | `POST /v1/contexts/{contextId}/fee-rules` |
| List fee rules for a context | `GET /v1/contexts/{contextId}/fee-rules`  |
| Retrieve a fee rule          | `GET /v1/fee-rules/{feeRuleId}`           |
| Update a fee rule            | `PATCH /v1/fee-rules/{feeRuleId}`         |
| Delete a fee rule            | `DELETE /v1/fee-rules/{feeRuleId}`        |

### Create a fee rule

This rule applies the fee schedule created above to right-side transactions whose `institution` metadata equals `Banco do Brasil`.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/fee-rules" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "side": "RIGHT",
   "feeScheduleId": "550e8400-e29b-41d4-a716-446655440000",
   "name": "BB Right-Side Processing Fee",
   "priority": 0,
   "predicates": [
     {
       "field": "institution",
       "operator": "EQUALS",
       "value": "Banco do Brasil"
     }
   ]
 }'
```

<Tip>
  API Reference:

  * [Create fee rule](/en/reference/matcher/create-fee-rule)
  * [List fee rules](/en/reference/matcher/list-fee-rules)
  * [Retrieve fee rule](/en/reference/matcher/retrieve-fee-rule)
  * [Update fee rule](/en/reference/matcher/update-fee-rule)
  * [Delete fee rule](/en/reference/matcher/delete-fee-rule)
</Tip>

## End-to-end flow

***

Putting it together, expected-fee handling follows three steps:

<Steps>
  <Step title="Create the fee schedule">
    Define how the fee is computed once at the tenant level with `POST /v1/fee-schedules`. Optionally validate it with the simulate endpoint. Note the returned `id`.
  </Step>

  <Step title="Create the fee rule in the context">
    Inside the reconciliation context, create a fee rule with `POST /v1/contexts/{contextId}/fee-rules`. Set `feeScheduleId` to the schedule's `id`, choose the `side`, set a unique `priority`, and add the `predicates` that select the transactions the fee applies to.
  </Step>

  <Step title="Apply during matching">
    Set the context's `feeNormalization` to `NET` or `GROSS`. During matching, Matcher evaluates rules for each relevant side in `priority` order. A matching rule changes the comparison amount only when its schedule and the transaction use the same currency.
  </Step>
</Steps>

## Best practices

***

<AccordionGroup>
  <Accordion title="Reuse schedules across contexts">
    A fee schedule is tenant-scoped and can be referenced by many rules. Define a schedule once (e.g. "Card Processing - Visa") and point rules in different contexts at it. Editing the schedule updates every rule that uses it.
  </Accordion>

  <Accordion title="Keep priorities unique and intentional">
    Priorities are unique within a context and shared across `LEFT`, `RIGHT`, and `ANY` rules. Order them from most specific to most general so a narrow rule wins before a broad fallback.
  </Accordion>

  <Accordion title="Scope rules with precise predicates">
    Predicates are ANDed together and tested against transaction metadata. Combine operators (`EQUALS`, `IN`, `BETWEEN`, `EXISTS`) to target exactly the transactions a fee applies to and avoid unintended fee attribution.
  </Accordion>

  <Accordion title="Simulate before wiring a schedule into a rule">
    Use `POST /v1/fee-schedules/{scheduleId}/simulate` to confirm a schedule produces the expected fee for representative amounts before referencing it from a rule.
  </Accordion>

  <Accordion title="Repoint rules before deleting a schedule">
    A schedule referenced by a rule or fee-variance history cannot be deleted (`409 MTCH-0108`). The conflict response does not list blocking contexts. Update or remove current rule references first; historical variance references continue to block deletion.
  </Accordion>
</AccordionGroup>

## Next steps

***

<Card title="Match rules" icon="scale-balanced" href="/en/matcher/configuration/matcher-match-rules" horizontal>
  Configure how transactions are compared once expected fees are accounted for.
</Card>

<Card title="Exception routing" icon="route" href="/en/matcher/configuration/matcher-exception-routing" horizontal>
  Review explicit assignment, dispatch, and callback behavior for exceptions that fee handling doesn't cover.
</Card>

<Card title="Fee schedules API" icon="code" href="/en/reference/matcher/create-fee-schedule" horizontal>
  Full API reference for fee schedule endpoints.
</Card>

<Card title="Fee rules API" icon="code" href="/en/reference/matcher/create-fee-rule" horizontal>
  Full API reference for fee rule endpoints.
</Card>
