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

# Resolving exceptions

> Review, prioritize, and resolve transactions Matcher couldn't reconcile automatically using severity, lifecycle, and audit-friendly actions.

Exceptions are transactions that Matcher can't reconcile automatically. This guide shows how to review exceptions, prioritize work based on severity, and resolve items with the right level of documentation.

## What is an exception?

***

An exception is created when a transaction from one source has no valid counterpart in another source. Common causes include:

* **No candidate found**: No transaction in the other source meets the active rule criteria.
* **Below confidence threshold**: Candidates exist, but score below the minimum confidence (default: 60).
* **Duplicate rejection**: A previous match was rejected and no alternative candidate remains.
* **Source imbalance**: One source contains transactions that are missing from the other.

## Exception lifecycle

***

Exceptions move through a simple workflow:

* When Matcher can't reconcile a transaction, it creates an exception in `OPEN` status.
* Assigning the exception moves it from `OPEN` to `ASSIGNED`. The API does not expose an unassign operation; `assignee` is required and must be non-empty.
* Force match and adjust entry persist `PENDING_RESOLUTION` only while the operation is in progress. Success moves the exception to `RESOLVED`; failure returns it to its previous `OPEN` or `ASSIGNED` status.
* Direct resolution moves an `OPEN` or `ASSIGNED` exception to `RESOLVED`.
* Dispatch sends the connector request, writes a `DISPATCH` audit event, and emits `exception.dispatched`. It does not change the exception status.

<Frame caption="The lifecycle of an exception in Matcher">
  <img src="https://mintcdn.com/lerian-49cb71fc/eJbUTctk-eLsW0J5/images/en/d2/matcher-exception-lifecycle.svg?fit=max&auto=format&n=eJbUTctk-eLsW0J5&q=85&s=69503da0352eca4a5625e9edeb50c176" alt="Matcher Exception Lifecycle" width="996" height="1130" data-path="images/en/d2/matcher-exception-lifecycle.svg" />
</Frame>

### Status definitions

| Status               | Description                                | Who Can Transition |
| -------------------- | ------------------------------------------ | ------------------ |
| `OPEN`               | New exception waiting for assignment       | System             |
| `ASSIGNED`           | Assigned to an analyst for investigation   | System, Analyst    |
| `PENDING_RESOLUTION` | Force match or adjust entry is in progress | System             |
| `RESOLVED`           | Closed with an auditable resolution        | Analyst, System    |

### State-machine endpoints

The following single-exception endpoints change the lifecycle or record related actions. Each is addressed by the exception's `exceptionId` in the path.

| Endpoint             | Method & path                                    | Purpose                                                                                                                                                                                                                                                                      |
| -------------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Assign exception     | `POST /v1/exceptions/{exceptionId}/assign`       | Assigns the exception to an analyst. Body: `assignee` (required). Returns the updated exception. (`OPEN` → `ASSIGNED`)                                                                                                                                                       |
| Dispatch exception   | `POST /v1/exceptions/{exceptionId}/dispatch`     | Sends the exception through the configured connector. Writes a `DISPATCH` audit event and emits `exception.dispatched` without changing status.                                                                                                                              |
| Resolve exception    | `POST /v1/exceptions/{exceptionId}/resolve`      | Resolves a single exception. Body: `resolution` (required), `reason` (optional). Mirrors bulk-resolve validation for one exception. (`OPEN` or `ASSIGNED` → `RESOLVED`)                                                                                                      |
| Force match          | `POST /v1/exceptions/{exceptionId}/force-match`  | Resolves an exception with `overrideReason` and `notes`. Uses `PENDING_RESOLUTION` while the operation is in progress, then resolves or returns to the previous status on failure.                                                                                           |
| Adjust entry         | `POST /v1/exceptions/{exceptionId}/adjust-entry` | Resolves an exception by creating an accounting adjustment entry. Body: `amount`, `currency`, `effectiveAt`, `notes`, `reasonCode` (all required). Uses `PENDING_RESOLUTION` while the operation is in progress, then resolves or returns to the previous status on failure. |
| Exception history    | `GET /v1/exceptions/{exceptionId}/history`       | Returns the ordered history of state transitions and actions for the exception (`HistoryResponse`). Supports `cursor`/`limit` pagination. *(read-only)*                                                                                                                      |
| Select exception IDs | `GET /v1/exceptions/ids`                         | Returns the full set of exception IDs matching the current filters (`contextId`, `status`, `severity`, `reason`, …). Use it to drive a bulk selection before calling the bulk endpoints. *(read-only)*                                                                       |

<Note>
  Matcher wires the webhook connector path and callback handling. JIRA connector code exists but is not configured by default. `MANUAL` acknowledges the dispatch locally without calling an external system. ServiceNow dispatch is not implemented: `SERVICENOW` reaches the generic unsupported-destination failure path and returns HTTP 500. See [Exception routing](/en/matcher/configuration/matcher-exception-routing) for the full dispatch contract.
</Note>

<Tip>
  API Reference:

  * [Assign exception](/en/reference/matcher/assign-exception)
  * [Dispatch exception](/en/reference/matcher/dispatch-exception)
  * [Resolve exception](/en/reference/matcher/resolve-exception)
  * [Force match](/en/reference/matcher/force-match-exception)
  * [Adjust entry](/en/reference/matcher/adjust-entry-exception)
  * [Get exception history](/en/reference/matcher/retrieve-exception-history)
  * [Select exception IDs](/en/reference/matcher/select-exception-ids)
</Tip>

#### Assign example

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/assign" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "assignee": "john.doe@company.com" }'
```

#### Resolve example

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/resolve" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "resolution": "ACCEPTED", "reason": "Variance within tolerance" }'
```

#### Adjust entry example

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/adjust-entry" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "amount": "150.50",
   "currency": "BRL",
   "effectiveAt": "2026-02-02T16:40:00Z",
   "reasonCode": "AMOUNT_CORRECTION",
   "notes": "Correcting processing fee discrepancy"
 }'
```

#### Bulk selection with `selectExceptionIDs`

```bash cURL theme={null}
curl -X GET "https://api.matcher.example.com/v1/exceptions/ids?contextId={contextId}&status=OPEN&severity=CRITICAL" \
 -H "Authorization: Bearer $TOKEN"
```

Feed the returned IDs into the [bulk operations](#bulk-operations) below.

## Exception severity

***

Matcher classifies exceptions by severity so you can work the queue in the right order.

| Severity     | Criteria                              | SLA                |
| ------------ | ------------------------------------- | ------------------ |
| **Critical** | Amount >= 100,000 OR age >= 120 hours | 24 hours           |
| **High**     | Amount >= 10,000 OR age >= 72 hours   | 72 hours           |
| **Medium**   | Amount >= 1,000 OR age >= 24 hours    | 120 hours (5 days) |
| **Low**      | All others                            | 168 hours (7 days) |

### Severity escalation

Severity is re-evaluated as an exception ages. The classification uses OR logic — either the amount or the age threshold is enough to trigger a higher severity:

* An exception under 1,000 starts as **Low**, but escalates to **Medium** after 24 hours.
* An exception under 10,000 escalates to **High** after 72 hours.
* Any unresolved exception escalates to **Critical** after 120 hours.

## Resolution methods

***

Matcher exposes three exception-resolution actions.

### 1. Resolve directly

Close an exception with a required `resolution` and an optional `reason` when no force match or adjustment is needed.

### 2. Force match

Manually link transactions when you've confirmed they belong together, but the system couldn't match them.

**Use Force Match when:**

* The correct counterpart exists, but variances blocked automatic matching.
* You can clearly explain and document the rationale.
* The variance is expected (fees, timing, rounding).

<Important>
  Force Match bypasses scoring and rule logic. Use it only when you can justify the decision in writing.
</Important>

### 3. Create adjustment

Create an adjusting entry to account for a variance or to balance an unmatched item.

**Adjustment reason codes:**

| Reason code           | Use case                             |
| --------------------- | ------------------------------------ |
| `AMOUNT_CORRECTION`   | Correct the transaction amount       |
| `CURRENCY_CORRECTION` | Correct the transaction currency     |
| `DATE_CORRECTION`     | Correct the effective date           |
| `OTHER`               | Record another documented correction |

**Validation rules:**

* Adjustment amounts must be positive. A request with a zero or negative amount returns a `400 Bad Request` error.
* `POST /v1/exceptions/{exceptionId}/adjust-entry` requires a valid ISO 4217 currency code. `POST /v1/matching/adjustments` accepts any non-empty currency string and does not validate ISO 4217 membership.
* `reasonCode` must use `AMOUNT_CORRECTION`, `CURRENCY_CORRECTION`, `DATE_CORRECTION`, or `OTHER`.

## Resolution records

***

Matcher records supported resolution actions in the exception history and audit stream.

| Resolution        | Request fields                                             |
| ----------------- | ---------------------------------------------------------- |
| Direct resolution | `resolution` (required), `reason` (optional)               |
| Force match       | `overrideReason`, `notes`                                  |
| Adjust entry      | `reasonCode`, `amount`, `currency`, `effectiveAt`, `notes` |

Matcher does not expose exception split or independent write-off resolution contracts, and it does not enforce amount-based approval thresholds for these actions. Apply any additional approval requirements through your organization's controls.

## Bulk operations

***

When dealing with large volumes of exceptions, bulk endpoints let you process up to 100 exceptions in a single request.

### Bulk assign

Assign multiple exceptions to a team member at once:

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/bulk/assign" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "exceptionIds": ["019c96a0-2a10-7dfe-b5c1-8a1b2c3d4e5f", "019c96a0-2b20-7123-9a1b-2c3d4e5f6a7b"],
   "assignee": "john.doe@company.com"
 }'
```

<Tip>
  API Reference:

  * [Bulk assign](/en/reference/matcher/bulk-assign-exceptions)
  * [Bulk resolve](/en/reference/matcher/bulk-resolve-exceptions)
  * [Bulk dispatch](/en/reference/matcher/bulk-dispatch-exceptions)
</Tip>

### Bulk resolve

Resolve multiple exceptions with a shared resolution:

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/bulk/resolve" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "exceptionIds": ["019c96a0-2a10-7dfe-b5c1-8a1b2c3d4e5f", "019c96a0-2b20-7123-9a1b-2c3d4e5f6a7b"],
   "resolution": "ACCEPTED",
   "reason": "Verified as valid bank fees"
 }'
```

The response includes `succeeded` and `failed` arrays, so you can handle partial failures gracefully.

### Bulk dispatch

Dispatch multiple exceptions to an external system:

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/bulk/dispatch" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "exceptionIds": ["019c96a0-2a10-7dfe-b5c1-8a1b2c3d4e5f"],
   "targetSystem": "WEBHOOK",
   "queue": "RECON-TEAM"
 }'
```

## Exception comments

***

Comments give each exception an audit trail of investigation notes and team discussion — invaluable when someone else has to pick up or review the case later. Add a comment as an analyst works an item:

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/comments" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "content": "Contacted bank to verify wire transfer fee. Awaiting confirmation."
 }'
```

Listing (`GET`) returns the full thread ordered oldest-first. You cannot add comments after an exception is resolved. Only the comment author can delete it, and the comment must belong to the exception identified in the URL.

| Action         | Method & path                                              | Key fields                                |
| -------------- | ---------------------------------------------------------- | ----------------------------------------- |
| Add comment    | `POST /v1/exceptions/{exceptionId}/comments`               | `content` (comment body)                  |
| List comments  | `GET /v1/exceptions/{exceptionId}/comments`                | — (returns the full thread, oldest first) |
| Delete comment | `DELETE /v1/exceptions/{exceptionId}/comments/{commentId}` | `commentId` in path                       |

<Tip>
  API Reference:

  * [List comments](/en/reference/matcher/list-exception-comments)
  * [Add comment](/en/reference/matcher/add-exception-comment)
  * [Delete comment](/en/reference/matcher/delete-exception-comment)
</Tip>

## Disputes

***

When an exception needs formal investigation or involves an external party — a chargeback, a bank query — escalate it to a **dispute**. Disputes track evidence, state changes, and the final outcome. List disputes with `GET /v1/disputes` (filter by `state`, e.g. `OPEN`) or retrieve one by its `disputeId`.

<Tip>
  API Reference:

  * [List disputes](/en/reference/matcher/list-disputes)
  * [Get dispute](/en/reference/matcher/retrieve-dispute)
  * [Open dispute](/en/reference/matcher/open-dispute)
  * [Close dispute](/en/reference/matcher/close-dispute)
</Tip>

### Dispute states and transitions

A dispute has five states: `DRAFT`, `OPEN`, `PENDING_EVIDENCE`, `WON`, and `LOST`. The flow is **not** strictly linear:

* `PENDING_EVIDENCE` is **optional** — an `OPEN` dispute can go directly to `WON` or `LOST` without ever collecting evidence.
* A `LOST` dispute can be **reopened** back to `OPEN`.
* `WON` is terminal.

The complete set of valid transitions:

| From state         | Allowed next states               | Notes                                                   |
| ------------------ | --------------------------------- | ------------------------------------------------------- |
| `DRAFT`            | `OPEN`                            | Dispute is opened for investigation                     |
| `OPEN`             | `PENDING_EVIDENCE`, `WON`, `LOST` | Can resolve directly, or request evidence first         |
| `PENDING_EVIDENCE` | `OPEN`, `WON`, `LOST`             | Returns to `OPEN` or resolves once evidence is reviewed |
| `WON`              | *(none)*                          | Terminal state                                          |
| `LOST`             | `OPEN`                            | A lost dispute can be reopened                          |

## Exception resolution workflow

***

Use this flow to keep reviews consistent and audit-friendly.

<Steps>
  <Step title="Triage">
    Review the queue by severity and SLA. Start with Critical and High.
  </Step>

  <Step title="Investigate">
    Use the exception payload to understand what failed and what candidates exist.

    * Read `reason_details` to see why matching failed.
    * Review `candidates` for near matches below threshold.
    * Look for patterns (same counterparty, recurring reference formats).
  </Step>

  <Step title="Resolve">
    Pick the resolution that best reflects reality and policy.

    * **Resolve directly**: You can close the exception without a force match or adjustment.
    * **Force Match**: You found the correct counterpart.
    * **Adjust**: You need an adjusting entry for variance.
  </Step>

  <Step title="Document">
    Capture enough detail for someone else to replay your decision later:

    * What you checked
    * What you concluded
    * Links or IDs for supporting evidence
  </Step>

  <Step title="Dispatch if needed">
    If the exception requires external handling, dispatch it through the configured webhook connector. Dispatch records the action but does not change the exception status. JIRA requires connector configuration that Matcher does not provide by default; ServiceNow is unavailable.
  </Step>
</Steps>

## Best practices

***

<AccordionGroup>
  <Accordion title="Work by severity and SLA">
    Start with Critical and High items. They carry the highest risk and the tightest deadlines.
  </Accordion>

  <Accordion title="Make decisions auditable">
    Notes aren't optional. Treat them as part of the resolution:

    * What you checked
    * Why this resolution is correct
    * Any ticket IDs, statements, or confirmations
  </Accordion>

  <Accordion title="Fix patterns at the source">
    Repeating exceptions usually point to configuration issues:

    * Same counterparty → Normalize names or mapping
    * Same date window → Validate ingestion completeness
    * Same source → Review field mapping and sign conventions
  </Accordion>

  <Accordion title="Treat force matches as exceptions to the rule">
    If you force-match regularly, your rules or tolerances need attention.
  </Accordion>

  <Accordion title="Assign work explicitly">
    Assign exceptions through the assignment endpoints. Matcher does not apply assignment rules automatically.
  </Accordion>
</AccordionGroup>

## Next steps

***

<Card title="Generating Reports" icon="chart-pie" href="/en/matcher/daily-reconciliation/matcher-generating-reports" horizontal>
  Create reconciliation reports, export results, and support audits.
</Card>

<Card title="Exception Routing" icon="route" href="/en/matcher/configuration/matcher-exception-routing" horizontal>
  Review severity, SLA, and routing concepts for exceptions.
</Card>
