Skip to main content
ReconLayer’s canonical model has six tables, but almost everything you’ll touch through the API maps to five core objects: PaymentIntent, RawRecord, FlowLeg, FlowLegReference, and ReconciliationCase, linked by MatchLink and narrated by AuditEvent. Every integration — whether it’s a direct API call, a webhook, a file import, or an on-chain feed — ultimately produces rows in these tables. This page defines each object precisely, in terms of the actual fields the API returns. For the end-to-end flow (how these objects come together for a single payment), see How ReconLayer Works. For matching and tolerance behavior, see Reconciliation Rules. For the underlying tables, see Database Architecture.
Every canonical field below has a corresponding entry in the Canonical Field Registry, which is also the contract import profiles use to map external files into these models.

1. PaymentIntent — what should happen

A PaymentIntent is the expectation: a client telling ReconLayer “this payment should occur, here’s what it should look like.” It is created via POST /v1/payment-intents, or indirectly from a client_internal_ledger file import. Key fields (from PaymentIntentCreateRequest / PaymentIntentSnapshot):
Creating a PaymentIntent always creates exactly one ReconciliationCase in the same transaction (status: 'open', expectedAmount copied from sourceAmount). The create response returns both paymentIntentId and caseId, with status: 'reconciling' and verdict: null — the case hasn’t been evaluated yet because no evidence has arrived.

Replay and conflict behavior

POSTing the same externalReference again is idempotent at the business level:
  • If the canonical fields (sourceAmount, sourceCurrency, destinationAmount, destinationCurrency, beneficiaryAccount, and — if previously set — stablecoin/chain) match the stored intent, the request is treated as a replay: the existing intent is updated (status, beneficiaryName, paymentType, etc.), a payment_intent.replayed audit event is written, and the response has outcome: 'reused'.
  • If those canonical fields don’t match, the service throws an ExpectationConflictError describing the field-by-field mismatches between the existing and incoming request — the client is asked to resolve the discrepancy rather than silently overwriting it.

2. RawRecord — what arrived, untouched

A RawRecord is the immutable evidence layer. Every inbound payload — a provider webhook, an on-chain indexer event, a row from an uploaded file, or a manual entry — is stored as one RawRecord before any normalization happens. RawRecord.payload is the audit anchor: when something looks wrong in a FlowLeg, you can always trace it back to the exact bytes that were ingested.

3. FlowLeg — where value actually moved

A FlowLeg is one normalized movement derived from a RawRecord. Complex cross-border payments produce multiple legs for a single PaymentIntent.

reconciliationScope and shouldMatchLeg

The reconciliation engine’s shouldMatchLeg() helper treats any leg with reconciliationScope: 'ignored' as invisible to matching — it is never selected as a match candidate and never blocks case completeness. required and optional legs are both eligible for matching; the distinction matters for requiredLegsTotal / requiredLegsPresent reporting on the case summary (see below).

4. FlowLegReference — typed long-tail identifiers

FlowLegReference rows attach typed identifiers to a FlowLeg without adding nullable columns to the leg table itself. Each row is { type, value }, e.g. { type: "bank_reference", value: "BANK-OUT-789" } or { type: "external_reference", value: "ACME-2026-0001" }. The matcher specifically looks for a leg reference with type: 'external_reference' as one of its candidate identifiers (see Reconciliation Rules).

5. ReconciliationCase — the truth row

Exactly one ReconciliationCase exists per PaymentIntent (1:1, enforced by a unique constraint on paymentIntentId). It is the record operators review, resolve, or archive.

The verdict field

verdict is not stored — it’s derived at read time from reconciliationStatus and exceptionType:
The export endpoint additionally supports sla_risk and delayed as verdict values for filtering (ReconciliationCaseExportVerdict enum: matched, matched_with_exception, needs_review, unreconciled, sla_risk, delayed). slaRisk itself is a separate boolean on the case summary: true when the case is open and has been open for more than 240 minutes (SLA_RISK_THRESHOLD_MINUTES).

ExceptionType enum

Evidence coverage and flow legs on the case summary

GET /v1/reconciliation-cases and GET /v1/reconciliation-cases/{caseId} both return a computed evidenceCoverage object and a flowLegs array summarizing the intent’s legs:
evidenceCoverage values are present | missing | warning | failed | not_provided, derived from leg status: confirmedpresent, pendingwarning, failed/reversedfailed, missingmissing, and not_provided when no leg of that type exists at all (file coverage is always not_provided today — no leg type maps to it yet). MatchLink is the explainable join between a ReconciliationCase, a FlowLeg, and a RawRecord. Each row records:
  • matchType: provider_id, tx_hash, reference_exact, amount_and_time_window, or manual_override.
  • matchReason: a human-readable sentence, e.g. “External reference ACME-2026-0001 matched the expected payment.”
  • confidence: deterministic, heuristic, or manual.
A (caseId, legId, rawRecordId) triple is unique — re-processing the same evidence against the same case is idempotent (upsert).

AuditEvent — the append-only narrative

Every meaningful state change writes an AuditEvent: payment_intent.created, payment_intent.replayed, match.created, match.none, case.evaluated, and more. Each event carries eventType, actor (e.g. system:matcher, user:<clerk-id>), an optional message, and a payload JSON blob with event-specific context. Audit events are surfaced on both PaymentIntentDetail.auditEvents and ReconciliationCaseDetail.auditEvents.

Putting it together

How ReconLayer Works

Conceptual walkthrough of the expectation → evidence → case lifecycle.

Reconciliation Rules

How tolerances, time windows, SLAs, and match strategies are configured.

Database Architecture

The Prisma schema behind these objects.

Canonical Field Registry

Every mappable field, by model and source type.