🎓 CoursesMarkets & internationalAdvanced⏱ 60 min

Reconciling multi-country payments. 7 chapters and a final quiz.

Close the books when your company collects payments in 10 countries. Read a camt.053, a BAI2, a CNAB 240, and a CFONB120 without picking the wrong field. Match an acquirer settlement report to the incoming transfer, choose between booking date and value date, reconcile a mobile money collection, and account for foreign exchange under IAS 21. Then automate without losing the audit trail.

Chapter 1. One canonical model, two statements: camt.053 and BAI2.

A company operating in 10 countries doesn't receive 10 comparable statements. It gets ISO 20022 XML from its German bank, BAI2 from its US bank, CNAB 240 from its Brazilian bank, and CFONB120 from its French bank: four grammars for saying that an account was credited. The temptation is to build one reconciliation engine per country, and within five years that choice costs more than any other. The approach that holds up separates two layers. On one side sit parsers, one per format, which all produce the same object, a canonical entry. On the other sits an engine that knows only that canonical entry. Adding a country then means writing a parser, not reworking the business rules. Here are the fields the canonical entry must carry.

  • Normalized account identifier: the IBAN where one exists, otherwise the (bank code, account number) pair plus the country
  • Account currency, in ISO 4217, never inferred from the country
  • Amount in minor units, as a signed integer: the sign comes from a format-specific rule, not from the generic parser
  • Explicit direction (credit or debit), redundant with the sign and checked against it
  • Booking date and value date: two separate fields, never merged
  • The original transaction code, kept as is, plus an internal canonical family (incoming transfer, direct debit, fee, reject…)
  • Bank reference: the identifier the bank can trace if there is a dispute
  • Originator reference (EndToEndId, customer reference, or Nosso Número, depending on the format)
  • Full raw description, not truncated, not normalized
  • Source file hash and line number, to get back to the evidence in a single query
FormatOrigin and where it's usedPhysical layoutAmount formatDates carried
camt.053ISO 20022, a global standard offered by banks in every regionSelf-describing XML treeDecimal, currency in the Ccy attribute, direction in CdtDbtIndBookgDt (booking) and ValDt (value)
BAI2Bank Administration Institute; North American banks, also used in AustraliaLines of comma-separated fields ending in /Integer with no decimal separator; the transaction type is set by the type codeGroup “as-of” date; availability via the Funds Type field
CNAB 240FEBRABAN, Brazil's bank-to-corporate file exchange standard240-position records, grouped into batches by serviceFixed-position integer, implied decimalsOccurrence date and credit date, carried by the segment
CFONB120CFONB, the French banking industry's statement format120-character records, no separators14 characters, the last of which encodes both a digit and the signBooking date and value date, in DDMMYY
The four statement families that feed the canonical model
🔑
The sign first, then the balance check
Each format encodes direction differently. camt.053 separates the amount from the CdtDbtInd indicator, BAI2 infers it from the type code, and CFONB120 locks it into the last character of the amount. A parser that copies an integer without applying the format's rule produces a wrong balance. The safeguard is the same everywhere: the opening balance plus the algebraic sum of the transactions must equal the closing balance. A file that doesn't balance doesn't enter the engine. Require every bank to supply a test file containing a debit, a credit, a reject, and a negative balance.

camt.053 is the ISO 20022 end-of-day statement. Its strength lies in three things that fixed-position formats can't carry: a currency attached to every amount, two separate dates, and a standardized transaction classification. Its weakness is its flexibility. Two banks can produce camt.053 files that are both valid and yet different, because each one decides what it actually structures and what it leaves as free text.

camt.053: a statement entry stripped down to the fields used for reconciliation
<Ntry>
  <Amt Ccy="EUR">12500.00</Amt>
  <CdtDbtInd>CRDT</CdtDbtInd>
  <BookgDt><Dt>2026-07-10</Dt></BookgDt>
  <ValDt><Dt>2026-07-13</Dt></ValDt>
  <AcctSvcrRef>0004512</AcctSvcrRef>
  <BkTxCd>
    <Domn>
      <Cd>PMNT</Cd>
      <Fmly><Cd>RCDT</Cd><SubFmlyCd>ESCT</SubFmlyCd></Fmly>
    </Domn>
  </BkTxCd>
  <NtryDtls>
    <TxDtls>
      <Refs><EndToEndId>ADYEN-SETTLEMENT-2026-BATCH42</EndToEndId></Refs>
    </TxDtls>
  </NtryDtls>
</Ntry>
  • With `Amt` and its `Ccy` attribute, the currency belongs to the amount, not to the account. A multicurrency account carries entries in different currencies.
  • `CdtDbtInd` is either CRDT or DBIT. The amount itself is always positive: converting it into a signed integer is the parser's job.
  • `BookgDt` and `ValDt` show, in this example, booking on Friday the 10th and value on Monday the 13th. For treasury, that's a three-day gap.
  • `BkTxCd` holds three levels drawn from the ISO 20022 external code lists: domain PMNT, family RCDT (received credit transfer), and subfamily ESCT. This is the field that lets you route an entry without reading the description.
  • `AcctSvcrRef` and `EndToEndId` give the reference the bank will be able to trace and the one the originator set. The second is the real key for automated matching.
In BAI2 (Bank Administration Institute, version 2, released in 1987), a file contains groups, a group contains accounts, and an account contains transactions
# Lines starting with # are annotations; they are not in the real file.
# 01 file header: sender, receiver, date, time, ID, version 2
01,BANKUS33,ACMECORP,260710,0800,001,,,2/
# 02 group header: as-of date of the report and group currency
02,ACMECORP,BANKUS33,1,260710,,USD,/
# 03 account + balances: type code 010 = opening balance, in CENTS
03,0012345678,USD,010,15234025,,/
# 16 transaction: type code 142 = ACH credit received; funds type Z = availability unknown
16,142,1250000,Z,0004512,PAYOUT-BATCH-42,ADYEN SETTLEMENT/
# 49 account trailer: control total = sum of the 03 and 16 amounts, then line count
49,16484025,3/
# 98 group trailer: total, number of accounts, number of lines in the group
98,16484025,1,5/
# 99 file trailer: total, number of groups, number of lines in the file
99,16484025,1,7/
ScopeValueWhat it means
Type code (balance)010Account opening balance
Type code (balance)015Account closing balance
Type code (balance)045Closing available balance; differs from the previous one if some funds are still unavailable
Type code (summary)100 / 400Total credits / total debits for the day
Funds Type0 / 1 / 2Funds available immediately, in one day, or in two or more days
Funds TypeS / DDistributed availability: the following fields break down the tranches
Funds TypeV / ZExplicit value date / availability not provided
The BAI2 codes a reconciliation reads first (amounts are always in minor units, with no decimal separator)
🎯 Quick question
In a BAI2 file, a type 16 record carries the amount 1250000 in a group declared in USD. What is the transaction worth?