Skip to main content
While ReconLayer is API-first, many counterparties and back offices still operate on daily batch files β€” internal ledgers, bank statements, PSP settlement reports, and on-chain transfer exports. The File Import pipeline turns those CSVs into the same PaymentIntent, RawRecord, and FlowLeg records produced by the API and webhook paths, so they flow through the same matching engine.
File imports are one of the three ways evidence enters ReconLayer. For provider-pushed evidence, see Inbound Webhooks and Bridge Integration.

Import lifecycle

1

Upload & batch creation

POST /v1/import-batches is called with an importProfileId, originalFileName, and the file content. ReconLayer computes fileHash = sha256:<hex> over the content. If a batch with the same fileHash already exists for the organization, the existing ImportBatch is returned with duplicate: true and no rows are reprocessed (response is 200 instead of 201).
2

Storage

The raw file is uploaded to object storage at imports/${organizationId}/${YYYY}/${MM}/${DD}/${fileHash}/${originalFileName}. This fileStorageKey is recorded on the ImportBatch.
3

Profile resolution

The referenced ImportProfile supplies sourceType, fieldMappings (CSV column β†’ canonical field path), optional valueMappings, and optional parsingRules (e.g. delimiter, dateFormat).
4

Parsing & normalization

The file is parsed as CSV (row 1 = header, data rows numbered from 2). Each data row is mapped through fieldMappings/valueMappings into canonical field values, validated, and recorded.
5

Downstream branching

  • If importProfile.sourceType === 'client_internal_ledger', each valid row is mapped to a PaymentIntent input and upserted (creating a ReconciliationCase if new, or detecting a conflict if an existing intent with the same externalReference differs).
  • For bank_statement, psp_report, and onchain_report, each valid row is mapped to a RawRecord + FlowLeg and passed through the same ingestEvidence pipeline used by webhooks and polling β€” including matching against open cases.
6

Completion

The ImportBatch is updated with totalRows, validRows, warningRows, failedRows, status, and completedAt. Per-row outcomes (including validation errors) are available via GET /v1/import-batches/{batchId}/raw-records.

Import Profiles

An ImportProfile is a reusable, named mapping configuration scoped to your organization. It tells ReconLayer:
FieldPurpose
sourceTypeOne of client_internal_ledger, bank_statement, psp_report, onchain_report (api_expectation and client_transfer_report are valid canonical field source types but cannot be used for import profiles).
fieldMappingsA Record<string, string> mapping your CSV column headers to canonical field paths, e.g. {"Settlement Amount": "FlowLeg.amount"}.
valueMappingsOptional per-field value-substitution maps β€” e.g. mapping a provider’s status strings ("Settled") onto ReconLayer enums ("confirmed").
parsingRulesOptional parsing configuration. Currently supports delimiter (single character, defaults to ,) and dateFormat for date/time fields.
provider / integrationKeyOptional β€” tag imported records with a provider name or integration key for downstream filtering.
isActiveWhether the profile can currently be used to create batches.
Every fieldMappings target is validated against the canonical field registry for the profile’s sourceType β€” mapping a column to an unsupported target for that source type returns 400 invalid_request.

Create an import profile

curl -X POST http://localhost:3001/v1/import-profiles \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Bank β€” Daily Settlement CSV",
    "sourceType": "bank_statement",
    "provider": "acme-bank",
    "fieldMappings": {
      "Txn Date": "FlowLeg.occurredAt",
      "Txn ID": "FlowLeg.providerTransferId",
      "Amount": "FlowLeg.amount",
      "Currency": "FlowLeg.currency",
      "Reference": "FlowLegReference.external_reference"
    },
    "valueMappings": {
      "Status": {
        "Posted": "confirmed",
        "Pending": "pending",
        "Returned": "reversed"
      }
    },
    "parsingRules": {
      "delimiter": ",",
      "dateFormat": "DD/MM/YYYY HH:mm:ss"
    },
    "isActive": true
  }'
The response is an ImportProfileDetail, which includes supportedTargets β€” the full list of canonical fields available for this sourceType, useful for building a mapping UI. See Create an Import Profile, List Import Profiles, Get Import Profile Detail, and Update an Import Profile.
PATCH /v1/import-profiles/{importProfileId} re-validates any updated fieldMappings against the profile’s existing sourceType β€” you cannot change sourceType and fieldMappings to an incompatible combination in the same request.

Canonical field registry

GET /v1/canonical-fields returns the full registry of fields ReconLayer understands, each with the models and source types it applies to. Use it to populate a column-mapping UI or to validate a profile before creating it.
curl "http://localhost:3001/v1/canonical-fields?sourceType=bank_statement&importTargetsOnly=true" \
  -H "Authorization: Bearer $RECONLAYER_API_KEY"
sourceType
string
Filter to fields available for one CanonicalFieldSourceType (client_transfer_report, client_internal_ledger, bank_statement, onchain_report, psp_report, manual, api_expectation).
model
string
Filter to fields on one CanonicalFieldModel (PaymentIntent, RawRecord, FlowLeg, FlowLegReference, ReconciliationCase).
importTargetsOnly
boolean
default:"false"
When true, restrict to fields usable as fieldMappings targets in an ImportProfile.
A sample entry:
{
  "path": "FlowLeg.amount",
  "model": "FlowLeg",
  "label": "Leg amount",
  "description": "The monetary amount moved on this leg.",
  "requirement": "required",
  "storage": "column",
  "sourceTypes": ["client_transfer_report", "bank_statement", "onchain_report", "psp_report", "manual"],
  "examples": ["1000.00", "0.5"]
}
client_internal_ledger rows map onto PaymentIntent.* fields (externalReference, sourceAmount, sourceCurrency, destinationAmount, destinationCurrency, paymentType, paymentSubtype, direction, effectiveDate, beneficiaryAccount, beneficiaryName, stablecoin, chain, references, metadata). Evidence source types (bank_statement, psp_report, onchain_report, plus client_transfer_report and manual) map onto RawRecord.* and FlowLeg.* fields β€” FlowLeg.type, .phase, .status, .amount, .providerTransferId, .txHash, .chainId, .fromAddress, .toAddress, .tokenAddress, and FlowLegReference.* for arbitrary key/value references. See List Canonical Fields.

Creating an import batch

POST /v1/import-batches accepts the file content inline (the schema requires fileContent; fileContentBase64 is accepted as an alternative encoding for binary-safe transport).
curl -X POST http://localhost:3001/v1/import-batches \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "importProfileId": "imp_profile_01h...",
    "originalFileName": "settlements-2026-06-14.csv",
    "format": "csv",
    "fileContent": "Txn Date,Txn ID,Amount,Currency,Reference,Status\n14/06/2026 09:30:00,TXN-1001,1000.00,USD,INV-2026-0099,Posted\n"
  }'
importProfileId
string
required
The profile that defines field mappings and parsing rules for this file.
originalFileName
string
required
Used for storage path and display.
fileContent
string
required
Raw file content as a string.
fileContentBase64
string
Alternative base64-encoded content.
fileHash
string
Override the computed sha256:<hex> dedup hash.
createdByUserId
string
Attribution for the dashboard activity log.
format
string
default:"csv"
Currently only csv is supported.
Response (201 for a new batch, 200 if the same fileHash was already imported):
{
  "batch": {
    "id": "imp_batch_01h...",
    "importProfileId": "imp_profile_01h...",
    "sourceType": "bank_statement",
    "provider": "acme-bank",
    "originalFileName": "settlements-2026-06-14.csv",
    "fileStorageKey": "imports/org_01h.../2026/06/14/sha256:.../settlements-2026-06-14.csv",
    "fileHash": "sha256:9f86d0...",
    "status": "completed",
    "totalRows": 1,
    "validRows": 1,
    "warningRows": 0,
    "failedRows": 0,
    "createdAt": "2026-06-14T09:30:05.000Z",
    "completedAt": "2026-06-14T09:30:06.000Z",
    "auditEvents": []
  },
  "duplicate": false
}
See Create an Import Batch.

Inspecting a batch and its rows

curl http://localhost:3001/v1/import-batches/imp_batch_01h... \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"
curl "http://localhost:3001/v1/import-batches/imp_batch_01h.../raw-records?limit=100" \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"
GET /v1/import-batches supports limit, offset, sourceType, and status (uploaded, mapping_required, parsing, validating, processing, completed, failed) for filtering. Each item in GET /v1/import-batches/{batchId}/raw-records is an ImportBatchRawRecordSnapshot:
{
  "id": "rr_01h...",
  "importBatchId": "imp_batch_01h...",
  "sourceRef": "file:imp_batch_01h...:TXN-1001",
  "rowNumber": 2,
  "normalizedType": "flow_leg",
  "normalizedId": "TXN-1001",
  "validationStatus": "valid",
  "validationErrors": null,
  "receivedAt": "2026-06-14T09:30:06.000Z",
  "payload": {
    "Txn Date": "14/06/2026 09:30:00",
    "Txn ID": "TXN-1001",
    "Amount": "1000.00",
    "Currency": "USD",
    "Reference": "INV-2026-0099",
    "Status": "Posted"
  }
}
normalizedType is payment_intent for client_internal_ledger rows and flow_leg for evidence rows. sourceRef for file-derived records follows the pattern file:{batchId}:{rowSourceRef-or-row-{rowNumber}}, which feeds the same (organizationId, source, sourceRef) dedup constraint used by webhooks and polling. See Get Import Batch Detail, List Import Batches, and List Import Batch Raw Records.

Default leg type and phase by source type

When a bank_statement, psp_report, or onchain_report row doesn’t explicitly map FlowLeg.type / FlowLeg.phase, ReconLayer applies these defaults:
sourceTypeDefault FlowLeg.typeDefault FlowLeg.phase
bank_statementbank_transferdestination
onchain_reportonchain_transfertransfer
psp_report (and others)provider_transferintermediary_in

Handling validation failures

A row fails validation if a required canonical field is missing, a value mapping has no match, or a date/amount fails to parse against parsingRules. Validation failures do not fail the whole batch:
  • Valid rows are processed normally and feed into the matching engine (or create PaymentIntents).
  • Failed rows are still recorded as ImportBatchRawRecordSnapshots with validationStatus: 'failed' and a validationErrors object describing what went wrong.
  • The batch’s totalRows, validRows, warningRows, and failedRows counters reflect the outcome so you can decide whether to re-upload a corrected file.
Re-uploading a corrected file produces a new ImportBatch (different fileHash) β€” it does not retroactively edit the failed rows of a previous batch. Use GET /v1/import-batches/{batchId}/raw-records to find the failing rows and fix them at the source before re-exporting.

Date and delimiter parsing

  • parsingRules.delimiter must be a single character; if omitted or invalid, ReconLayer defaults to ,.
  • parsingRules.dateFormat controls how date/time strings are normalized. ReconLayer accepts standard ISO 8601 timestamps regardless of dateFormat, and additionally supports DD/MM/YYYY HH:mm:ss when configured.
  • The CSV parser handles quoted values (including embedded delimiters and quotes) per standard CSV quoting rules. Row 1 is always treated as the header row; data rows are numbered starting from 2 for rowNumber in raw record snapshots.

Next steps

  • Ingestion Flows β€” how imported rows feed the matching engine alongside webhooks and API evidence.
  • Inbound Webhooks and Bridge Integration β€” for evidence that arrives in real time instead of in batch files.
  • Outbound Webhooks β€” subscribe to payment_intent.created to be notified when client_internal_ledger imports register new expectations.