---
title: "Company Rematch"
method: POST
path: "/companies/{company_id}/reconciliation/rematch"
tags: ["reconciliation"]
---

# Company Rematch

`POST /companies/{company_id}/reconciliation/rematch`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/companies/{company_id}/reconciliation/rematch`
- Auth: none declared

Re-run the matcher over every entry that could still gain a link.

The selection spans **two** independent state columns on purpose (ZET-179). An entry
that matched on the document axis but never reached the settlement plane reads
``reconciliation_status=matched`` with its whole amount in Suspense — invisible to a
reconciliation-status filter, which is exactly how 131 entries stayed stuck. The
settlement arms are the worker claim's own ``pending_settlement_criteria`` (ZET-189),
so this route and the background sweep can never disagree about which entries are
recoverable — the ``matched`` + ``unsettled`` cell matched *neither* of this route's
previous arms, leaving the operator with no way to reach the exact population the
defect strands.

Still link-only — it never posts. Draining the Suspense those links unlock is
``/reconciliation/resettle``, which is privileged accordingly.

Neither this route nor ``/resettle`` eager-loads the entry's link graph, and neither
may start to: both return counts, and nothing in ``match_bank_entry`` /
``autonomous_settle`` reads a relationship off the entry — every one of them goes
through an explicit aggregate query, which is why the background sweep's own claim
(``claim_reconciliation_entries``) selects bare rows too. The seven ``selectinload``
chains that used to sit here (``_ENTRY_LOADS``, built for the *serialising* GET
routes) pulled each entry's documents and their whole raw analysis JSON for nobody to
read (ZCT-546).

``limit`` bounds the batch exactly as ``/resettle``'s does (review finding: the
widened population made an unbounded scan-and-eager-load possible on a backlogged
company — thousands of rows with seven selectinload chains inside one request
transaction). Deterministic ``ORDER BY id``; run repeatedly to walk the rest.

ZCT-546 — read this before believing the paragraph above. That review finding was
only half the cost, and the half it fixed was never the expensive one: the selection
here **is** bounded (``limit=1`` issues two statements), but each entry's own matching
pass then re-scanned the company's *whole* backlog — one round-trip per item, one per
journal entry and up to three per open item, plus an eager-load of every document's
raw analysis JSON — so the per-entry cost scaled with the backlog and ``limit``
multiplied it rather than bounding it. Measured on a seeded 230-item / 150-journal /
120-open-item company: 794 statements and 5.9 s at ``limit=1``, and 15,235 statements
and **119 s** at ``limit=20`` — on localhost, before any network latency. Batched, the
same calls cost 53 and 529 statements, ~25 per entry independent of the backlog, so
the cost of a call is proportional to ``limit`` again.

One caveat kept deliberately: this bounded the cost, it did not measure the reporter's
machine. ``limit=1`` at ~6 s locally does not on its own reach any deployed timeout
(gunicorn 120 s, nginx and the MCP client 600 s, no ``statement_timeout``), so if a
``limit=1`` call still hangs after this, suspect the blocking ``with_for_update()`` in
``_locked_open_amount`` rather than the scan volume.

## Path parameters

- `company_id` string, required

## Query parameters

- `limit` integer

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Response `202`

Successful Response

- RematchResponse
  - `msg` string, required
  - `linked` integer, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
