Skip to main content
ReconLayer’s core job is to compare what should happen (an expectation) against what actually happened (evidence), and link the two together as a 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 immutable RawRecord.
There is no single “ingestion endpoint.” Instead, there are three independent entry points for evidence, plus direct API creation of expectations, and ReconLayer’s matching engine runs after each one to see whether an expectation and its evidence can now be linked.
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, a POST /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:
  1. Checks for an existing RawRecord with the same (organizationId, source, sourceRef) and short-circuits as a duplicate if found. The response sets duplicate: true and returns the original rawRecordId, flowLegIds, and matchedCaseIds.
  2. Persists the payload as an immutable RawRecord and logs an evidence.received audit event.
  3. Creates one or more FlowLeg rows (and any FlowLegReferences) from the normalized legs, logging a leg.normalized audit event for each.
  4. 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.
All of these paths return (or wrap) the same EvidenceAcceptanceResponse shape:
{
  "accepted": true,
  "duplicate": false,
  "rawRecordId": "rr_01h...",
  "flowLegIds": ["fl_01h..."],
  "matchedCaseIds": ["case_01h..."],
  "signatureValid": true,
  "source": "webhook",
  "sourceRef": "evt_abc123"
}

Flow 1: Expectation-first

Use this path when the thing arriving first tells ReconLayer what should happen. Typical sources:
  • A direct POST /v1/payment-intents call from your backend.
  • A client_internal_ledger row in a file import.
Sequence:
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, or onchain_report row in a file import.
  • An on-chain integration ingest (POST /v1/onchain-integrations/{onchainIntegrationId}/ingest).
  • A direct POST /v1/evidence/provider or POST /v1/evidence/onchain call.
Sequence:
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 an Idempotency-Key header. Replaying the same key with the same body returns the original response with header Idempotency-Replayed: true; replaying with a different body returns 409 idempotency_key_conflict; a concurrent in-flight request with the same key returns 409 idempotency_request_in_progress.
  • At the ingestEvidence layer — used by webhooks, polling, and imports too — duplicates are detected independently via the (organizationId, source, sourceRef) uniqueness constraint on RawRecord, so re-delivered webhooks, re-polled transfers, and re-uploaded files are all safe to retry.
  • POST /v1/import-batches is itself idempotent on file content: re-uploading the same bytes for the same organization returns the existing ImportBatch with duplicate: true instead of reprocessing rows.

Where each entry point lands

Entry pointSource typeProduces
POST /v1/payment-intentsPaymentIntent + ReconciliationCase
POST /v1/import-batches (client_internal_ledger)client_internal_ledgerPaymentIntent + ReconciliationCase
POST /v1/import-batches (bank_statement / psp_report / onchain_report)per profileRawRecord + FlowLeg(s)
POST /webhooks/bridge/{integrationKey}psp_report (+ onchain_transfer leg when applicable)RawRecord + FlowLeg(s)
POST /v1/evidence/providerpsp_reportRawRecord + FlowLeg
POST /v1/evidence/onchainonchain_reportRawRecord + FlowLeg
POST /v1/bridge-integrations/{id}/poll-transfer (+ background poll loop)psp_report (API source)RawRecord + FlowLeg(s)
POST /v1/onchain-integrations/{id}/ingestonchain_reportRawRecord + FlowLeg
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.