ReconciliationCase. Everything that flows into the system — API calls, provider webhooks, on-chain events, CSV uploads — ultimately resolves into one of two record types:
- A
PaymentIntent— an expectation that a payment will move. - A
FlowLeg— a normalized piece of evidence describing one leg of a payment’s actual journey (a bank credit, a provider transfer, an on-chain transfer, etc.), backed by an immutableRawRecord.
This page is the map. For the mechanics of each entry point, see File Imports, Inbound Webhooks, Outbound Webhooks, Bridge Integration, and On-chain Integrations.
The three ways evidence enters ReconLayer
Every evidence-shaped payload — whether it arrives as a Bridge webhook, aPOST /v1/evidence/provider call, an on-chain ingest, or a row in an imported file — is normalized into the same shape (a RawRecord plus zero or more NormalizedLegs) and passed through a single ingestEvidence pipeline. That pipeline:
- Checks for an existing
RawRecordwith the same(organizationId, source, sourceRef)and short-circuits as a duplicate if found. The response setsduplicate: trueand returns the originalrawRecordId,flowLegIds, andmatchedCaseIds. - Persists the payload as an immutable
RawRecordand logs anevidence.receivedaudit event. - Creates one or more
FlowLegrows (and anyFlowLegReferences) from the normalized legs, logging aleg.normalizedaudit event for each. - Runs the matching engine against open
ReconciliationCases — unless the record failed signature validation (signatureValid === false), in which case matching is skipped entirely and the evidence sits unmatched for manual review.
EvidenceAcceptanceResponse shape:
Flow 1: Expectation-first
Use this path when the thing arriving first tells ReconLayer what should happen. Typical sources:- A direct
POST /v1/payment-intentscall from your backend. - A
client_internal_ledgerrow in a file import.
1
Expectation is registered
A
PaymentIntent is created — directly via the API, or via an imported ledger row. ReconLayer opens a ReconciliationCase with status: open, logs a payment_intent.created audit event, and queues a payment_intent.created outbound webhook event for any subscribed endpoints.2
ReconLayer waits
No evidence exists yet. The case sits open until a matching
FlowLeg arrives.3
Evidence arrives later
A provider webhook, on-chain event, or file import produces a
RawRecord and one or more FlowLegs via ingestEvidence.4
Matching runs
The matching engine searches for a
PaymentIntent / ReconciliationCase that the new FlowLeg(s) satisfy — for example by externalReference, providerTransferId, or txHash. If found, a MatchLink is created and the case is updated.Flow 2: Evidence-first
Use this path when the thing arriving first tells ReconLayer what actually happened, before any expectation was registered. Typical sources:- A Bridge webhook at
POST /webhooks/bridge/{integrationKey}. - A
bank_statement,psp_report, oronchain_reportrow in a file import. - An on-chain integration ingest (
POST /v1/onchain-integrations/{onchainIntegrationId}/ingest). - A direct
POST /v1/evidence/providerorPOST /v1/evidence/onchaincall.
1
Evidence arrives
The provider or chain payload is normalized and passed to
ingestEvidence, creating a RawRecord and one or more FlowLegs.2
Matching engine searches for an expectation
The engine looks for an open
ReconciliationCase / PaymentIntent that this evidence could satisfy.3
Matched
If a match is found, a
MatchLink is created linking the FlowLeg(s) to the ReconciliationCase, and the case is updated.4
Unmatched
If no match is found, the
FlowLeg(s) remain in the system as unresolved evidence. They are not discarded — a later expectation (or a corrected import) can still match against them.Signature validation short-circuits matching
For evidence that carries a cryptographic signature (currently Bridge webhooks),ingestEvidence still creates the RawRecord and FlowLegs even if the signature is invalid — but it skips the matching step (matchedCaseIds will be empty) and marks the RawRecord with validationStatus: 'failed'. The HTTP response for POST /webhooks/bridge/{integrationKey} returns 400 invalid_webhook_signature in this case, while still reporting the rawRecordId and sourceRef that were persisted for audit purposes. See Inbound Webhooks for details.
Idempotency and duplicates
- The canonical evidence endpoints (
POST /v1/evidence/provider,POST /v1/evidence/onchain) require anIdempotency-Keyheader. Replaying the same key with the same body returns the original response with headerIdempotency-Replayed: true; replaying with a different body returns409 idempotency_key_conflict; a concurrent in-flight request with the same key returns409 idempotency_request_in_progress. - At the
ingestEvidencelayer — used by webhooks, polling, and imports too — duplicates are detected independently via the(organizationId, source, sourceRef)uniqueness constraint onRawRecord, so re-delivered webhooks, re-polled transfers, and re-uploaded files are all safe to retry. POST /v1/import-batchesis itself idempotent on file content: re-uploading the same bytes for the same organization returns the existingImportBatchwithduplicate: trueinstead of reprocessing rows.
Where each entry point lands
Continue to File Imports for the batch path, Inbound Webhooks and Bridge Integration for provider-pushed evidence, On-chain Integrations for chain indexer evidence, and Outbound Webhooks for how ReconLayer notifies you once an expectation is registered.
