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

# Reviewing matches

> Review matches after a run — understand the 0–100 confidence score, its four weighted components, variance causes, and how to reject or revoke a match group.

After running a matching job, you'll need to review the results. This guide explains how to interpret match results, understand confidence scores, and reject proposed or revoke confirmed match groups.

## Match status lifecycle

***

Matches progress through a defined lifecycle:

* When the matching engine finds a pair of transactions that belong together, it creates a match in `PROPOSED` status.
* Eligible engine-created matches (score 90 or above) from EXACT and TOLERANCE rules are auto-confirmed immediately. Manual matches are created in `CONFIRMED` status. FUZZY and DATE\_LAG matches remain `PROPOSED` and are never auto-confirmed.
* Matches that are not auto-confirmed remain in `PROPOSED`. The public match-group API does not expose manual confirmation, but you can reject a proposed group with the unmatch operation and a required reason.
* Rejecting a `PROPOSED` group returns its transactions to the unmatched pool. A `CONFIRMED` group can be revoked only when Matcher can also reverse every residual/open-item effect that confirmation applied; on success, those changes and the transaction return happen atomically.

<Note>
  **FUZZY and DATE\_LAG matches are never auto-confirmed.** Auto-confirmation at score ≥ 90 applies to eligible engine-created EXACT and TOLERANCE matches. A manual match is created in `CONFIRMED` status. A match produced by a FUZZY or DATE\_LAG rule stays in `PROPOSED`, regardless of its score — even a FUZZY match scoring 90+. You can reject it through the unmatch operation; there is no public manual-confirm operation. See [Confidence scoring](/en/matcher/reference/matcher-confidence-scoring#fuzzy-matches-never-auto-confirm).
</Note>

<Frame caption="Status lifecycle of a match.">
  <img src="https://mintcdn.com/lerian-49cb71fc/eJbUTctk-eLsW0J5/images/en/d2/matcher-match-status.svg?fit=max&auto=format&n=eJbUTctk-eLsW0J5&q=85&s=dc72053489e1132a8cd2dcde512d064d" alt="Match Status Lifecycle" width="555" height="975" data-path="images/en/d2/matcher-match-status.svg" />
</Frame>

### Status definitions

| Status      | Description                                           | Next Actions                                      |
| ----------- | ----------------------------------------------------- | ------------------------------------------------- |
| `PROPOSED`  | Match identified by the system but not confirmed      | Review or reject through the unmatch operation    |
| `CONFIRMED` | Match was auto-confirmed or created as a manual match | Revoke through the unmatch operation if incorrect |
| `REJECTED`  | Match was declined                                    | Transactions return to unmatched pool             |
| `REVOKED`   | A previously confirmed match was revoked              | Transactions return to unmatched pool             |

## Confidence tiers

***

Matcher assigns a confidence score (0-100) to each proposed match. The score determines how the match is handled.

### Confidence levels

| Tier                  | Score range | Behavior                                                                                                                                                                                      |
| --------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Auto-confirm eligible | 90-100      | Eligible engine-created EXACT and TOLERANCE matches are automatically confirmed without manual review. Manual matches are created `CONFIRMED`; FUZZY and DATE\_LAG matches remain `PROPOSED`. |
| Needs Review          | 60-89       | Medium confidence matches remain `PROPOSED`. You can review and reject them, but the public API does not expose manual confirmation.                                                          |
| No Match              | Below 60    | Low confidence candidates are not proposed as matches and become exceptions.                                                                                                                  |

### Understanding the score

The confidence score is calculated from weighted components:

| Component       | Weight | What it Measures                      |
| --------------- | ------ | ------------------------------------- |
| Amount match    | 40%    | How closely transaction amounts align |
| Currency match  | 30%    | Whether currencies are the same       |
| Date tolerance  | 20%    | How close the transaction dates are   |
| Reference match | 10%    | Whether transaction references match  |

**Example Score Breakdown:**

```
Match: BANK-001 ↔ LED-001

Amount: $1,000.00 vs $1,000.00 → 100% × 40% = 40 points
Currency: USD vs USD → 100% × 30% = 30 points
Date: 2024-01-15 vs 2024-01-15 → 100% × 20% = 20 points
Reference: PAY-001 vs PAY-001 → match → 10 points
 ─────────────────────────
Total Confidence: 100 points
```

## Understanding variances

***

When matches have differences, review the variance details:

### Amount variance

Common causes of amount variance:

* Bank fees
* Currency conversion differences
* Rounding differences
* Partial payments

### Date variance

Common causes of date variance:

* Settlement timing
* Time zone differences
* Posting vs. transaction date
* Weekend/holiday processing

## Match candidates, open items, and adjustments

***

As you work a review queue, three questions come up again and again: *why did the engine propose this pairing?*, *what's still unsettled after a partial match?*, and *how do I book a small difference so both sides balance?* Matcher answers each with a dedicated surface — match candidates, open items, and adjustments.

### List match candidates

Retrieve the ranked candidate proposals the engine considered for a transaction — the "why" behind a proposed match, including the per-component contributions.

```bash cURL theme={null}
curl -X GET "https://api.matcher.example.com/v1/matching/candidates?contextId={contextId}&transactionId={transactionId}&limit=20" \
 -H "Authorization: Bearer $TOKEN"
```

`GET /v1/matching/candidates` accepts `contextId`, `transactionId`, and `limit` query parameters and returns a `CandidateProposalsResponse`. The `limit` defaults to 50 and accepts values from 1 to 200.

The candidate picker scans at most 5,000 unmatched transactions from the opposite side and returns only 1:1 candidates. It applies the shared scorer to raw transaction data, without the run-time fee normalization or FX variance-band matching used by a matching run.

### List open items

Open items are residual balances left when a transaction is only partially netted. Track them to surface amounts that still need clearing or that have aged past their threshold.

```bash cURL theme={null}
curl -X GET "https://api.matcher.example.com/v1/matching/contexts/{contextId}/open-items?status=OPEN&limit=50" \
 -H "Authorization: Bearer $TOKEN"
```

`GET /v1/matching/contexts/{contextId}/open-items` supports a `status` filter, plus `limit`/`cursor` pagination.

#### Open item statuses

| Status              | When it occurs                                                                                                                                                             |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OPEN`              | Fresh residual — the item has a remaining balance and no leg has netted against it yet.                                                                                    |
| `PARTIALLY_CLEARED` | At least one leg has netted against the balance, but a residual still remains.                                                                                             |
| `CLEARED`           | The residual has been fully netted within tolerance.                                                                                                                       |
| `AGED`              | The item remained open beyond its aging threshold, measured from the obligation business date or the legacy first-seen time.                                               |
| `WITHDRAWN`         | A confirmed-group unmatch removed the final live contribution behind the obligation. The item remains as history, but is terminal and cannot be netted or carried forward. |

The normal lifecycle flows `OPEN` → `PARTIALLY_CLEARED` → `CLEARED`, with any still-open item able to become `AGED` once it passes the aging threshold. A successful confirmed-group unmatch withdraws that group's residual effects. If no live contribution remains behind the obligation, the item ends `WITHDRAWN` — it was taken back, not cleared or aged.

### Create an adjustment

Post an accounting adjustment to account for a variance (bank fee, FX difference, rounding, write-off, and so on) against a match group or transaction.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/matching/adjustments?contextId={contextId}" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "amount": "10.50",
   "currency": "BRL",
   "direction": "DEBIT",
   "type": "BANK_FEE",
   "reason": "Variance due to bank processing fee",
   "description": "Bank wire fee adjustment",
   "matchGroupId": "019c96a0-0b74-768c-8d25-2bf065dca2f8"
 }'
```

`POST /v1/matching/adjustments` requires the `contextId` query parameter. Body fields:

<ParamField path="amount" type="String" required>
  Adjustment amount
</ParamField>

<ParamField path="currency" type="String" required>
  Non-empty currency string. Matcher does not validate ISO 4217 membership for this adjustment.
</ParamField>

<ParamField path="direction" type="String" required>
  `DEBIT` or `CREDIT`
</ParamField>

<ParamField path="type" type="String" required>
  `BANK_FEE`, `FX_DIFFERENCE`, `ROUNDING`, `WRITE_OFF`, or `MISCELLANEOUS`
</ParamField>

<ParamField path="reason" type="String" required>
  Business reason for the adjustment
</ParamField>

<ParamField path="description" type="String" required>
  Human-readable description
</ParamField>

<ParamField path="matchGroupId" type="UUID">
  Match group the adjustment applies to
</ParamField>

<ParamField path="transactionId" type="UUID">
  Transaction the adjustment applies to
</ParamField>

At least one of `matchGroupId` or `transactionId` is required.

<Tip>
  API Reference:

  * [List match candidates](/en/reference/matcher/list-match-candidates)
  * [List open items](/en/reference/matcher/list-open-items)
  * [Create adjustment](/en/reference/matcher/create-adjustment)
</Tip>

## Rejecting or revoking match groups

***

Use the **unmatch** operation to reject a `PROPOSED` group or revoke a `CONFIRMED` group. After a successful operation, Matcher returns the group's transactions to `UNMATCHED` so they can be matched again.

Use `DELETE /v1/matching/groups/{matchGroupId}` with the required `contextId` query parameter and a `reason` in the request body (operationId `unmatch`):

```bash cURL theme={null}
curl -X DELETE "https://api.matcher.example.com/v1/matching/groups/{matchGroupId}?contextId={contextId}" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "reason": "incorrect match - amounts do not match"
 }'
```

A successful unmatch returns **204 No Content**. The `reason` field is required (non-empty). For a `CONFIRMED` group, Matcher verifies the residual/open-item reversal before changing the group or its transactions; a successful operation atomically appends the compensating ledger entries, revokes the group, and returns its transactions to `UNMATCHED`.

<Warning>
  The unmatch operation requires a non-empty reason. For a confirmed group, it either reverses the residual/open-item effects together with the group and its transactions, or returns **409 Conflict** before changing anything. A later live entry on the residual blocks the earlier reversal; a newer live obligation on the same identity also blocks it when restoring a terminal item would conflict.
</Warning>

### When to reject or revoke

Common scenarios for rejecting or revoking a group:

* **Incorrect match confirmed**: The match was confirmed but the transactions belong to different records
* **New information**: Additional data shows the match is wrong
* **Source correction**: The source system issued a correction or reversal
* **Duplicate transaction**: One of the transactions was a duplicate that should be removed

### What happens after unmatch

When a match group is unmatched:

1. **Confirmed residuals are checked first**: Matcher verifies that every residual/open-item effect of a `CONFIRMED` group can be reversed. If a later entry or a conflicting newer obligation blocks it, the operation returns `409 Conflict` and leaves the group, transactions, and open items unchanged.
2. **Match status changes**: A `CONFIRMED` group becomes `REVOKED`; a still-`PROPOSED` group becomes `REJECTED`.
3. **Transactions returned**: On success, all associated transactions revert to `UNMATCHED` status.
4. **Open-item effects reversed**: On a confirmed-group unmatch, compensating ledger entries take back the group's residual effects in the same transaction. If that leaves no live contribution behind an obligation, it becomes terminal `WITHDRAWN` and is not carried forward.
5. **Reason recorded**: The group stores the supplied rejection or revocation reason.
6. **Streaming event emitted**: A `match_group.unmatched` event is emitted only when the group was previously `CONFIRMED`.
7. **Re-matching possible**: Transactions can be matched again in the next run after a successful unmatch.

## Best practices

***

<AccordionGroup>
  <Accordion title="Start with lowest confidence matches">
    Review matches with the lowest confidence scores first. These are most likely to be incorrect and need the most attention.
  </Accordion>

  <Accordion title="Understand fixed confidence thresholds">
    Confidence thresholds are fixed in the system. Eligible engine-created EXACT and TOLERANCE matches with scores of 90 or above are auto-confirmed. Manual matches are created `CONFIRMED`. Scores between 60 and 89 remain `PROPOSED`, and scores below 60 become exceptions. These values are not configurable per context. **FUZZY and DATE\_LAG matches are the exception: they are never auto-confirmed and remain `PROPOSED`, regardless of score.** Use rule tuning (priority, tolerance values) to influence how many matches land in each tier.
  </Accordion>

  <Accordion title="Give a clear reason">
    Provide a specific reason when rejecting a proposed group or revoking a confirmed group. The unmatch operation requires it and stores it with the group.
  </Accordion>

  <Accordion title="Reject only after checking the source data">
    Review the group and its source transactions before using the unmatch operation. Matcher exposes this operation per group; it does not expose bulk confirmation.
  </Accordion>

  <Accordion title="Review high-value matches carefully">
    Regardless of confidence score, give extra attention to high-value matches. The impact of an incorrect match is proportional to the amount.
  </Accordion>
</AccordionGroup>

## Next steps

***

<Card title="Resolving exceptions" icon="triangle-exclamation" href="/en/matcher/daily-reconciliation/matcher-resolving-exceptions" horizontal>
  Handle transactions that couldn't be matched automatically.
</Card>

<Card title="Confidence scoring" icon="chart-simple" href="/en/matcher/reference/matcher-confidence-scoring" horizontal>
  Deep dive into how confidence scores are calculated.
</Card>
