> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reconifyhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Reconify's API reference is read-only for customer data. Do not invent endpoints or authentication behavior beyond the OpenAPI contract.
> The public OpenAPI document contains only the documented external /v2 contract.

# Output and schemas

> Output formats, the result and summary documents, run_info, and schema identifiers.

## Formats

| Format        | Memory characteristic                                                                     | Use for                                                        |
| ------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `json`        | Buffers the full result before writing.                                                   | Smaller files, integrations that want one complete document.   |
| `json-stream` | Encodes events as they're processed. May be invalid JSON if the process is interrupted.   | Lower memory pressure while still wanting JSON-like structure. |
| `ndjson`      | One tagged event per line. O(1) for result writing, crash-safe for already-written lines. | Large files, streaming pipelines, scheduled jobs.              |
| `csv`         | Fixed-schema, flat.                                                                       | Spreadsheet inspection, downstream tabular processing.         |
| `table`       | Buffers rows for display.                                                                 | Interactive human inspection only.                             |

`--audit` is supported for `json`, `json-stream`, and `ndjson`, not `csv` or `table`. `--deterministic` only affects buffered `json` output. For `rights` multi-counterpart runs, `json`, `json-stream`, and `ndjson` include a per-counterpart breakdown; `csv` and `table` include only the aggregate result.

## The NDJSON envelope

Every NDJSON line carries the same envelope shape:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"schema": "reconify.engine.result.v1", "type": "match", "data": {}}
```

| Field    | Description                                              |
| -------- | -------------------------------------------------------- |
| `schema` | Always `reconify.engine.result.v1` for reconcile output. |
| `type`   | The event name. See the list below.                      |
| `data`   | The event payload.                                       |

Event names are stable and are not renamed:

`run_info`, `index_selection`, `match`, `amount_diff`, `timing_diff`, `unmatched_left`, `unmatched_right`, `duplicate`, `grouped_match`, `grouped_amount_diff`, `grouped_timing_diff`, `many_to_many_match`, `many_to_many_amount_diff`, `many_to_many_timing_diff`, `ambiguous_group`, `source_summary`, `summary`.

## The result document

`json` and `json-stream` output share this top-level shape:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "schema": "reconify.engine.result.v1",
  "pair": "left_source_vs_right_source",
  "left_source": "left_source",
  "right_source": "right_source",
  "summary": {
    "currency": "NGN",
    "total_left": 842,
    "total_right": 851,
    "matched": 810,
    "unmatched_left": 12,
    "unmatched_right": 18,
    "amount_diff_count": 6,
    "timing_diff_count": 4,
    "duplicate_count": 2,
    "match_rate_pct": 96.2,
    "reconciled_rate_pct": 97.6,
    "matched_amount_left": 184230000,
    "matched_amount_right": 184230000,
    "unmatched_amount_left": 1500000,
    "unmatched_amount_right": 2200000,
    "amount_diff_total": 340000,
    "total_discrepancy": 4040000
  },
  "matched": [],
  "unmatched_left": [],
  "unmatched_right": [],
  "amount_diff": [],
  "timing_diff": [],
  "duplicates": [],
  "grouped_matched": [],
  "grouped_amount_diff": [],
  "grouped_timing_diff": [],
  "many_to_many_matched": [],
  "many_to_many_amount_diff": [],
  "many_to_many_timing_diff": [],
  "ambiguous_groups": [],
  "warnings": []
}
```

`184230000` is `1,842,300.00`: every monetary field is minor units.

| Field                                                                          | Description                                                                                                                                                                 |
| ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema`                                                                       | Always `reconify.engine.result.v1`.                                                                                                                                         |
| `run_info`                                                                     | Present only under `--audit` or `--auto`. See [`run_info`](#run_info) below.                                                                                                |
| `index_selection`                                                              | Present when the run recorded which index backend was chosen and why.                                                                                                       |
| `pair`, `left_source`, `right_source`                                          | The pair and source names from the config.                                                                                                                                  |
| `summary`                                                                      | Aggregate counts and totals. See [The summary object](#the-summary-object).                                                                                                 |
| `matched`                                                                      | One-to-one matches: same reference, amount within tolerance, date within window.                                                                                            |
| `unmatched_left` / `unmatched_right`                                           | Rows present on one side with no counterpart on the other.                                                                                                                  |
| `amount_diff`                                                                  | Same reference, date fine, amounts disagree beyond tolerance.                                                                                                               |
| `timing_diff`                                                                  | Same reference, amounts agree, dates outside the window.                                                                                                                    |
| `duplicates`                                                                   | Rows in the same source sharing a group key, annotated for visibility. Every row still participates in matching; a duplicate group can include a row that matched normally. |
| `grouped_matched`, `grouped_amount_diff`, `grouped_timing_diff`                | Populated only by an `one_to_many` pass.                                                                                                                                    |
| `many_to_many_matched`, `many_to_many_amount_diff`, `many_to_many_timing_diff` | Populated only by a `many_to_many` pass.                                                                                                                                    |
| `ambiguous_groups`                                                             | Groups where grouping was undetermined; excluded from matching and requiring manual resolution.                                                                             |
| `warnings`                                                                     | Non-fatal observations, such as empty-currency rows mixed with a non-empty base currency.                                                                                   |
| `by_source`                                                                    | Populated only for `rights` multi-counterpart runs. See [Per-counterpart summaries](#per-counterpart-summaries).                                                            |

## The summary object

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "currency": "NGN",
  "total_left": 842,
  "total_right": 851,
  "matched": 810,
  "unmatched_left": 12,
  "unmatched_right": 18,
  "amount_diff_count": 6,
  "timing_diff_count": 4,
  "duplicate_count": 2,
  "match_rate_pct": 96.2,
  "reconciled_rate_pct": 97.6,
  "matched_amount_left": 184230000,
  "matched_amount_right": 184230000,
  "unmatched_amount_left": 1500000,
  "unmatched_amount_right": 2200000,
  "amount_diff_total": 340000,
  "total_discrepancy": 4040000
}
```

`result_mode` and `run_id` are omitted from this object entirely unless they apply: `result_mode` only appears when a run sets it explicitly (per pair, or with `--result-mode`), and `run_id` only when telemetry is active.

Similarly, `grouped_matched`, `grouped_amount_diff`, `grouped_timing_diff`, `many_to_many_matched`, `many_to_many_amount_diff`, `many_to_many_timing_diff`, `ambiguous_group_count`, `ambiguous_amount_left`, and `ambiguous_amount_right` only appear when the pair configures the matching passes that produce them.

| Field                                                                                                       | Description                                                                                                                                                                                                                                                                                                                                   |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `result_mode`                                                                                               | Present only when set. The emission mode applied: `all`, `exceptions_only`, or `summary_only`.                                                                                                                                                                                                                                                |
| `currency`                                                                                                  | The base currency for monetary totals. Empty when no transaction carried a currency.                                                                                                                                                                                                                                                          |
| `run_id`                                                                                                    | Present only when telemetry is active. The telemetry run identifier.                                                                                                                                                                                                                                                                          |
| `total_left`, `total_right`                                                                                 | Row counts parsed from each source.                                                                                                                                                                                                                                                                                                           |
| `matched`, `unmatched_left`, `unmatched_right`, `amount_diff_count`, `timing_diff_count`, `duplicate_count` | Outcome counts. `duplicate_count` counts transactions across all duplicate groups, not the number of groups.                                                                                                                                                                                                                                  |
| `match_rate_pct`                                                                                            | Percentage of exact one-to-one reference matches only, of `max(total_left, total_right)`.                                                                                                                                                                                                                                                     |
| `reconciled_rate_pct`                                                                                       | Percentage including amount diffs, timing diffs, and grouped outcomes: every case where the two sides were reconciled to each other, just not perfectly. Also of `max(total_left, total_right)`. A `one_to_many` pass inflates `total_right`, so a fully reconciled grouped dataset can legitimately report a sub-100% `reconciled_rate_pct`. |
| `grouped_matched`, `grouped_amount_diff`, `grouped_timing_diff`                                             | Present only with a `one_to_many` pass configured. Counts from that pass.                                                                                                                                                                                                                                                                     |
| `many_to_many_matched`, `many_to_many_amount_diff`, `many_to_many_timing_diff`                              | Present only with a `many_to_many` pass configured. Counts from that pass.                                                                                                                                                                                                                                                                    |
| `ambiguous_group_count`                                                                                     | Present only when relevant. Groups excluded from matching because grouping was undetermined.                                                                                                                                                                                                                                                  |
| `matched_amount_left`, `matched_amount_right`                                                               | Minor-unit totals of matched rows on each side.                                                                                                                                                                                                                                                                                               |
| `unmatched_amount_left`, `unmatched_amount_right`                                                           | Minor-unit totals of unmatched rows on each side.                                                                                                                                                                                                                                                                                             |
| `amount_diff_total`                                                                                         | Minor-unit sum of amount differences.                                                                                                                                                                                                                                                                                                         |
| `ambiguous_amount_left`, `ambiguous_amount_right`                                                           | Present only when relevant. Minor-unit totals from ambiguous groups.                                                                                                                                                                                                                                                                          |
| `total_discrepancy`                                                                                         | `unmatched_amount_left + unmatched_amount_right + amount_diff_total + ambiguous_amount_left + ambiguous_amount_right` (the last two are `0` when absent). Zero with the expected `matched` count means a clean run; non-zero is exactly the money still unexplained.                                                                          |

All monetary fields are minor units, and are always computed from the full reconciliation, even when `result_mode` suppresses item events.

## `run_info`

Present when `--audit` is set, or when `reconcile --auto` is used:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "run_id": "01HZXQ",
  "timestamp": "2026-01-15T09:32:00Z",
  "tool_version": "0.4.0",
  "left_file": {
    "path": "path/to/left-file.csv",
    "sha256": "e3b0c4...",
    "size_bytes": 184320,
    "mod_time": "2026-01-14T22:00:00Z"
  },
  "right_file": {
    "path": "path/to/right-file.csv",
    "sha256": "9f86d0...",
    "size_bytes": 92160,
    "mod_time": "2026-01-14T21:45:00Z"
  },
  "pair_config": {
    "date_window": "1d",
    "amount_tolerance_minor": 0,
    "name_mode": "none"
  },
  "inferred_config": null,
  "inference_confidence": null
}
```

| Field                                 | Description                                                                                        |
| ------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `run_id`, `timestamp`, `tool_version` | Run identifier, RFC3339 timestamp, and the binary version that produced the result.                |
| `left_file`, `right_file`             | `path`, `sha256`, `size_bytes`, `mod_time` for each input file.                                    |
| `pair_config`                         | Snapshot of the applied `date_window`, `amount_tolerance_minor`, and `name_mode`.                  |
| `inferred_config`                     | Populated only for `--auto` runs: the exact YAML config used, so the run is reproducible non-auto. |
| `inference_confidence`                | Populated only for `--auto` runs: each selected mapping's column, confidence, and confidence lead. |

## Per-counterpart summaries

`json` and `json-stream` include a per-counterpart `by_source` object alongside the aggregate `summary` for `rights` runs:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "pair": "ledger_vs_settlements",
  "summary": {
    "total_left": 1200,
    "matched": 1180,
    "reconciled_rate_pct": 98.33
  },
  "by_source": {
    "right_source": {
      "total_left": 1200,
      "total_right": 900,
      "matched": 890,
      "unmatched_left": 310,
      "unmatched_right": 10,
      "reconciled_rate_pct": 74.17
    },
    "paypal_settlements": {
      "total_left": 310,
      "total_right": 298,
      "matched": 290,
      "unmatched_left": 20,
      "unmatched_right": 8,
      "reconciled_rate_pct": 93.55
    }
  }
}
```

`ndjson` emits the same breakdown as one `source_summary` line per counterpart, followed by the aggregate `summary` line:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"type":"source_summary","data":{"source":"right_source","summary":{"total_left":1200,"total_right":900,"matched":890,"reconciled_rate_pct":74.17}}}
{"type":"summary","data":{"total_left":1200,"matched":1180,"reconciled_rate_pct":98.33}}
```

`csv` and `table` omit the per-counterpart breakdown and include only the aggregate result.

## `result_mode`

| Value             | Emits                                                                                                                              |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `all`             | Every event, including clean matches. Default.                                                                                     |
| `exceptions_only` | Unmatched rows, amount/timing diffs, duplicates, ambiguous groups, and grouped/N:M exception events. Clean matches are suppressed. |
| `summary_only`    | Only the final summary. All item events are suppressed.                                                                            |

Set `result_mode` per pair in `reconify.yaml`, or override at runtime with `--result-mode`; the flag wins. Filtering happens at the writer boundary: classification counts, monetary totals, and `currency` are always computed from the full reconciliation. Suppressing events never changes the numbers.

## Schema identifiers

| Identifier                           | Emitted by                                                          |
| ------------------------------------ | ------------------------------------------------------------------- |
| `reconify.engine.result.v1`          | `reconcile` (JSON/json-stream top level, and every NDJSON envelope) |
| `reconify.engine.diagnostic.v1`      | Any command, on error, when `--error-format json` or `--agent`      |
| `reconify.engine.capabilities.v1`    | `capabilities`                                                      |
| `reconify.engine.profile.v1`         | `inspect`                                                           |
| `reconify.engine.config-proposal.v1` | `config infer`                                                      |
| `reconify.engine.explanation.v1`     | `explain`                                                           |

`reconify schema <name>` prints the published Draft 2020-12 document for each identifier (for example, `reconify schema result`). Versioning rule: new fields under a `vN` identifier are additive and safe for a consumer to ignore; a breaking change creates a new `v2` identifier rather than reusing `v1` for an incompatible shape.

## Diagnostics

When `--error-format json` or `--agent` is set, a failed command emits this envelope to stderr:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "amount column not found",
  "code": "config_error",
  "ok": false,
  "schema": "reconify.engine.diagnostic.v1",
  "diagnostic": {
    "code": "AMOUNT_COLUMN_NOT_FOUND",
    "category": "config",
    "message": "amount column \"Amount\" not found in path/to/left-file.csv",
    "details": { "column": "Amount", "source": "left_source" },
    "suggestions": ["Run `reconify inspect path/to/left-file.csv` to see the actual column names."]
  }
}
```

The legacy `error` and `code` fields remain present and unchanged in meaning, for callers that only understand the original contract. `diagnostic.code` can be more granular. `suggestions` exists specifically so an agent has a next action instead of a dead end.

## Exit codes

| Code | Meaning                                                                                        |
| ---- | ---------------------------------------------------------------------------------------------- |
| `0`  | Success.                                                                                       |
| `1`  | Unexpected or internal error.                                                                  |
| `2`  | Config or validation error.                                                                    |
| `3`  | Reconcile completed with unmatched rows (`--fail-if-unmatched` only).                          |
| `4`  | Reconcile completed with exception events (`--fail-if-exceptions`; takes precedence over `3`). |

`3` and `4` are not failures: the run completed and found unmatched rows or exceptions. They only fire when you've opted in with `--fail-if-unmatched` or `--fail-if-exceptions`.
