POST /v1/reconcile

POST /v1/reconcile

Match the candidates from a camt.053 parse against your expected-payments list. The closed-loop step.

Takes the candidate-key list from POST /v1/iso20022/camt.053/parse plus a list of expected payments (typically your open invoices or outbound payroll lines) and produces per-entry matching outcomes.

Outcomes: matched (with the matched expected-payment id, confidence high), ambiguous (top three candidates ranked, confidence medium), unmatched (no candidate above the floor), out_of_band (entry has no candidate — typically a bank fee or interest credit).

The matching uses (carrier confidence) × (value-equality) × (amount-match-within-tolerance) as a composite score; tolerance is configurable per tenant.

See apps/api/src/routes/reconcile.ts and the matcher at apps/api/src/lib/reconcile.ts.

Request

Request body

{
  "entries": "Array<{ entry_ref, amount, currency, credit_debit, candidates: Array<{ value, carrier, confidence }> }>",
  "expected_payments": "Array<{ id, amount, currency, references: string[] }>",
  "amount_tolerance": "number (optional, default 0)"
}

Response

Response body

{
  "matches": "Array<{ entry_ref, outcome: \"matched\" | \"ambiguous\" | \"unmatched\" | \"out_of_band\", expected_payment_id?, score?, top_candidates?: Array<{ id, score }> }>"
}

Idempotency

Mandatory header `Idempotency-Key` (UUID or other opaque ≤64 char string). A second request with the same key and the same body returns the cached response and the header `X-Iso-Compliant-Idempotent-Replay: true`. A second request with the same key but a different body returns 409.

Rate limit

Sandbox: 60 requests / minute, 1000 / day. Production: 600 requests / minute soft cap, lifted per tenant on request.

← All docs