The first time a German finance colleague said “we need the CAMT files in Salesforce,” I nodded confidently and then went home and read for three hours. If you’re in that same spot right now — someone just handed you an acronym and an expectation — this article is for you. By the end, you’ll know what CAMT.053 is, why finance teams depend on it, and how I built a daily reconciliation flow for a real B2B Quote-to-Cash project that matched bank payments to invoices without ever double-posting.
What ISO 20022 and CAMT.053 are, in plain words
ISO 20022 is a family of standardized message formats that banks and financial systems use to talk to each other. Think of it as a shared vocabulary: instead of every bank inventing its own file format, they all agree on the same XML structures.
CAMT.053 is one specific message in that family. It’s the end-of-day bank statement. Every business day, your bank produces a CAMT.053 file that says: “Here is your account, here was the opening balance, here is every transaction that happened today — who paid, how much, and what they wrote in the reference line — and here is the closing balance.”
That’s it. It’s not exotic. It’s a bank statement, in XML, with a very formal dress code.
Why German finance teams live on this
In Germany (and much of Europe), bank-to-business communication runs on these standards. The finance team doesn’t log into online banking and eyeball transactions — their systems ingest CAMT.053 files automatically, every morning, and reconcile them against open invoices.
Here’s the business problem underneath it. You sent a customer an invoice for €1,190. At some point they pay it by bank transfer. The money lands in your account — but your accounting system doesn’t magically know which invoice that payment belongs to. Until someone (or something) connects the payment to the invoice, that invoice stays “open,” dunning emails threaten loyal customers, and the finance team can’t close the books.
Matching payments to invoices is called reconciliation, and doing it by hand is soul-crushing at any real volume. That’s why the CAMT.053 file exists: it’s machine-readable, so a machine can do the matching.
On my TechnoStore project — a B2B Quote-to-Cash build integrating ten external systems through MuleSoft and Apex REST — this was one of the flows the finance team cared about most. Not the flashy AI features. The bank statement.
What a CAMT.053 entry looks like
Here’s a heavily simplified sketch of one transaction entry, just so the shape isn’t a mystery. Real files are much noisier — this is the teaching version:
<!-- Simplified for readability — real CAMT.053 has many more elements -->
<Ntry>
<Amt Ccy="EUR">1190.00</Amt>
<CdtDbtInd>CRDT</CdtDbtInd> <!-- credit = money in -->
<BookgDt><Dt>2026-06-09</Dt></BookgDt>
<NtryDtls>
<TxDtls>
<RltdPties>
<Dbtr><Nm>Muster GmbH</Nm></Dbtr> <!-- who paid -->
</RltdPties>
<RmtInf>
<Ustrd>INV-2026-00417</Ustrd> <!-- the reference line -->
</RmtInf>
</TxDtls>
</NtryDtls>
</Ntry>
Four things matter for matching: the amount, the direction (credit or debit), the date, and the remittance information — that <RmtInf> block, which is whatever the payer typed into the reference field of their bank transfer. When they type your invoice number in there, your day is easy. When they type “thanks Klaus,” it isn’t.
The daily reconciliation flow
Here’s the flow I built, end to end. It runs every morning:
1. Statement in
The bank statement arrives — in our case, pulled through the MuleSoft layer on a schedule. The raw XML never touches Salesforce directly; MuleSoft receives it, validates it, and hands Salesforce clean, structured entries.
2. Parse the entries
Each <Ntry> becomes a Bank Transaction record in Salesforce: amount, currency, direction, booking date, payer name, and the raw remittance text. Storing the raw text matters — when matching fails, a human needs to see exactly what the payer wrote.
3. Match to invoices
For each incoming credit, the matcher tries to find the open invoice it pays. More on the strategies in a moment.
4. Exceptions go to humans
Anything the matcher can’t confidently resolve lands in an exception queue — a list view of unmatched transactions with all the context attached. A finance person reviews them, picks the right invoice manually, and moves on.
This last step is the honest part of the design. I did not try to build a matcher that resolves 100% of payments, because that matcher doesn’t exist. I built one that resolves the easy 90% automatically and makes the hard 10% fast for a human. That’s the realistic goal, and finance teams respect it far more than a “fully automated” system that silently guesses wrong.
Matching strategies: reference first, then fallback
The matcher works in tiers, from most confident to least:
Tier 1: reference match
Scan the remittance text for something that looks like one of your invoice numbers. If INV-2026-00417 appears in the reference and that invoice is open for exactly the amount received — match, done, high confidence. In pseudo-code:
// Pseudo-code, simplified
for each open invoice number:
if remittanceText contains invoiceNumber
and invoice.amountDue == transaction.amount:
match with HIGH confidence
This is why finance teams beg customers to include the invoice number in their transfer. When they do, matching is trivial.
Tier 2: amount + date fallback
No usable reference? Fall back to circumstantial evidence: is there exactly one open invoice for this customer (matched by payer name or IBAN) with this exact amount, invoiced within a reasonable window before the payment date? If yes, match with medium confidence — and in our build, medium-confidence matches were still surfaced for a quick human confirm rather than auto-posted.
The key word is exactly one. If two open invoices both say €1,190, don’t guess. Guessing wrong is worse than not matching at all.
Tier 3: the exception queue
Everything else goes to humans. Partial payments, bundled payments covering three invoices at once, payments from a parent company on behalf of a subsidiary — these are real, they’re common, and a person with context resolves them in seconds where an algorithm would embarrass itself.
Idempotency: re-imports must not double-post
Here’s the trap that will hurt you if you skip it. Bank files get re-delivered. Someone re-runs the morning job because it “looked like it failed.” MuleSoft retries after a timeout. If your import blindly creates a Bank Transaction record every time it sees an entry, one re-run and suddenly every payment exists twice — and every invoice looks overpaid. Explaining that to a Steuerberater is not a conversation you want.
The cure is an idempotency key: a unique fingerprint per transaction entry, stored on the record in an external-ID field. CAMT.053 gives you good raw material — the account, the booking date, the amount, and the bank’s own entry reference. Combine them into one key:
// Pseudo-code
key = hash(accountIban + entryReference + bookingDate + amount)
if BankTransaction exists with IdempotencyKey == key:
skip — we've already imported this one
else:
create the record
With that in place, you can re-import the same statement ten times and get exactly one set of records. Re-runs become boring — which is exactly what you want. This is the same idempotency principle I wrote about in error handling and retries; bank reconciliation is where it stops being theory and starts protecting real money.
What I’d tell you to remember
CAMT.053 sounds intimidating and isn’t. It’s a daily bank statement in XML. The engineering that matters is around it: parse into clean records, match by reference first and amount-plus-date second, refuse to guess when confidence is low, route exceptions to humans with full context, and make the whole import idempotent so re-runs can never double-post.
Build it that way, and the finance team will trust the system — and in my experience, a finance team that trusts your integration is the best ally a Salesforce developer can have.
Your next step
The matching output eventually feeds the accountant’s world — in our project, a DATEV export for the Steuerberater. Before that, make sure your foundations are solid: Error Handling and Retries covers the assume-failure mindset this flow depends on, and authentication basics covers keeping the bank connection secure with Named Credentials. The full journey lives in the Integration category.