Bulk Payments in ISO 20022: Batching, BatchBooking, and What Arrives
Written by Ahmed at Analyst Engineering, a Senior Technical Business Analyst with 10+ years in banking and payments delivery.
Key takeaways
- A pain.001 has three levels: the group header covering the whole file, payment information blocks that carry the debit account and execution date, and transactions inside them. Almost every bulk payment defect comes from putting a field at the wrong level.
- BatchBooking decides what the payer sees on their statement: true means one aggregated debit per payment information block, false means one debit per transaction. It is a reconciliation decision, not a technical preference, and the payer's accounting team should choose it.
- The debit account and requested execution date live at the payment information level, not the transaction level. A file paying from two accounts or on two dates needs two payment information blocks, and systems that assume one block per file break on the first real payroll.
- Specify partial failure explicitly. When one of eleven thousand payments is invalid, the file can be rejected wholly, processed with that one rejected, or held for repair, and each produces a completely different Monday morning for the client.
- The status report is the deliverable, not the acknowledgement. A pain.002 that says the file was received tells a corporate nothing useful; they need to know which transactions were accepted, which were rejected, and why, keyed by their own references.
A pain.001 has three levels, and the debit account and execution date live at the middle one. BatchBooking decides whether the payer sees one statement line or eleven thousand, which is their accounting team’s decision. Specify partial failure explicitly, because whole-file rejection and partial processing produce very different Monday mornings. And the status report is the deliverable, not the acknowledgement.
A corporate submits a payroll file on Thursday for Friday. One employee’s IBAN is wrong. On Friday morning either nobody is paid, or 10,999 people are paid and one is not, and the treasurer finds out from an employee rather than from the bank.
Which of those happens was decided months earlier, by whether anyone wrote down the partial failure rule. This continues The ISO 20022 Reference, and it is the article for anyone building or integrating a corporate payment channel.
The three levels, and why they matter
A pain.001 nests three levels, and getting a field’s level wrong is the defining bulk payment defect.
CstmrCdtTrfInitn
├── GrpHdr ONCE for the whole file
│ ├── MsgId the file identifier
│ ├── CreDtTm when it was created
│ ├── NbOfTxs, CtrlSum control totals
│ └── InitgPty who sent it
└── PmtInf (repeating) PER debit account + execution date
├── PmtInfId
├── PmtMtd TRF
├── BtchBookg true or false
├── ReqdExctnDt the execution date
├── Dbtr, DbtrAcct, DbtrAgt WHO PAYS, from WHICH account
├── ChrgBr the charge bearer
└── CdtTrfTxInf (repeating) PER beneficiary payment
├── PmtId (EndToEndId) the client's own reference
├── Amt
├── Cdtr, CdtrAcct, CdtrAgt
├── Purp
└── RmtInf
The critical placement: the debit account and the requested execution date are at the payment information level, not at the transaction level.
That has a consequence teams miss constantly. A file paying from two accounts needs two PmtInf blocks. A file with payments on two different dates needs two PmtInf blocks. A file doing both needs four.
Systems built on the assumption of one PmtInf per file work perfectly against simple test files and fail on the first real payroll, which typically pays from a dedicated salary account on one date and expenses from an operating account on another. Build the multi-block case into your test data from day one.
The charge bearer also sits at PmtInf level, though some guidelines permit it per transaction. Check the net rule for your scheme, exactly as in reading a usage guideline.
BatchBooking: an accounting decision wearing technical clothes
BtchBookg is a boolean that changes nothing for beneficiaries and everything for the payer.
true: the bank books one aggregated debit per PmtInf block. A payroll run of 11,000 payments totalling 4,218,940.55 appears on the statement as a single line.
false: each transaction is booked individually. 11,000 statement lines.
Neither is correct in general. It depends entirely on how the payer reconciles:
| Payer situation | Better choice |
|---|---|
| Ledger holds one payroll journal entry | true |
| Ledger holds a payable per employee or supplier | false |
| Statement volume limits or fees per line | true |
| Automated per-invoice cash application | false |
| Salary confidentiality on a shared statement | true, with SALA |
That last row matters. With batch booking on and the category purpose set to SALA, individual salaries do not appear as statement lines that anyone with account access can read. With batch booking off, every salary is a visible line item. That is a data protection consequence of a boolean.
So the decision belongs to the payer’s accounting team, and the onboarding conversation should ask it explicitly rather than accepting whatever the file happens to contain. A surprising number of corporates have never been asked and do not know the field exists.
How a batch appears on the statement
This connects directly to reading a camt.053, and it is where the most expensive integration defect in bulk payments lives.
With batch booking on, the statement shows:
Ntry 4,218,940.55 DBIT <- one entry, moves the balance
└── NtryDtls
├── TxDtls employee 1 <- the individual payments
├── TxDtls employee 2
└── ... 10,998 more
With batch booking off, there are 11,000 entries.
A reconciliation system that reads Ntry and ignores NtryDtls sees one payment in the first case. It is the same defect from the receiving side too: a beneficiary whose bank books incoming collections as a batch will find one credit where there were four hundred customer payments.
So the rule for any statement integration: always descend into transaction details. Treat the entry as a booking event and the transaction detail as the payment. Test it with a batched file, because a test set built from single payments will never expose it.
Specify partial failure before it happens
Back to the payroll file with one wrong IBAN. Three possible behaviours, each defensible:
Reject the whole file. The safest for control and the worst for a deadline. Nobody is paid, the client fixes one record and resubmits, and if they miss the cut-off nobody is paid on time either.
Process the valid, reject the invalid. 10,999 people are paid. One is not. The client must detect which one and handle it, which means the status reporting has to be good enough to tell them, and their process has to be capable of acting on it.
Hold for repair. The file waits while someone fixes it. Only useful if the repair can happen inside the cut-off, and it needs a human available.
Validation level matters too, and it is a separate question: some failures are structural (the file is malformed, control totals do not match) and cannot be partial, while others are per-transaction (an invalid IBAN, a restricted character, an amount above a limit) and can be.
Write the matrix: failure type, level, behaviour, notification, and who the client contacts. Then get the client to sign it. This is the single most valuable artifact in a corporate channel implementation, and its absence is why these conversations happen at 07:00 on a payday.
Control totals deserve a specific mention. NbOfTxs and CtrlSum exist so a mismatch between what the file says it contains and what it actually contains is caught immediately. Validate them, always, and reject on mismatch, because a file that has been truncated in transit will otherwise process partially and silently.
The status report is the deliverable
A pain.002 that says “file received” is an acknowledgement. What a corporate needs is a report they can act on:
- Status at group level: was the file accepted at all.
- Status at payment information level: was each block accepted.
- Status at transaction level for every rejection, with the reason code and the client’s own
EndToEndId.
That last point is the one that decides whether the report is usable. The bank knows the payment by its own reference. The client knows it by theirs. A rejection reported with only the bank’s identifier forces the client to reconcile manually against a file of eleven thousand rows, which is exactly the work the channel was supposed to remove.
Then map the reason codes to something a human can act on. AC01 is not a customer message; “the account number is invalid, check the IBAN for this employee” is. Reason code mapping covers building that layer, and it is the difference between a channel clients like and one they tolerate.
What to test
- A file with multiple
PmtInfblocks: two debit accounts, two execution dates. BtchBookgtrue and false, asserting the resulting statement structure in each case, including that transaction details are present under a batched entry.- One invalid transaction among many, asserting the agreed partial failure behaviour exactly.
- A structural failure, asserting whole-file rejection.
- Control totals deliberately wrong, asserting rejection.
- A duplicate
MsgIdresubmitted, asserting duplicate detection. Files get resubmitted by nervous humans, and a double payroll is a very bad day. Idempotency testing covers proving this. - A batched salary file with
SALA, asserting both the aggregation and the confidentiality treatment. - A pain.002 for a partially rejected file, asserting the client’s own references appear against the rejections.
- A file at realistic volume, because behaviour at eleven thousand transactions differs from behaviour at ten, particularly for timeouts and statement generation.
That duplicate case is worth putting near the top of the list. The most common cause is a client who did not receive an acknowledgement quickly enough and sent the file again.
The takeaway
Three levels, and the debit account and execution date sit at the middle one, so any real file needs multiple payment information blocks. BatchBooking decides whether the payer reconciles against one line or eleven thousand, and it carries a salary confidentiality consequence, so ask the client rather than defaulting.
Any statement integration must descend into transaction details, or a batched entry looks like one payment. Specify the partial failure matrix and have the client sign it, because the alternative is deciding it live on a payday. And make the status report carry the client’s own references, or it is not actionable.
Next: choosing the right payment rail, which brings the scheme track together. The full map is on The ISO 20022 Reference.
Ahmed is a Senior Technical Business Analyst with 10+ years in banking and payments. He builds practical guides and tools for analysts at The Tech BA Toolkit.
Tags: ISO 20022, Payments, Corporate Banking, Functional Analysis, Reconciliation
About the author
Analyst Engineering is written by Ahmed, a Senior Technical Business Analyst with 10+ years of banking and payments delivery experience: ISO 20022 and SWIFT messaging, payments API integration, Kafka event validation, and production support. Every article comes from real delivery work, and each one is reviewed and updated as tools and standards change.
Related articles
- Reading a camt.053: The Bank Statement an Analyst Can Reconcile How a camt.053 is structured: balance types, entries, entry details, bank transaction codes, and the references that let you match a statement line to a payment.
- ISO 20022 Purpose Codes: Purp, CtgyPurp, and What They Drive Downstream Purpose code vs category purpose in ISO 20022: who reads each, the codes that change routing and treatment, and why a wrong SALA costs a payroll run.
- SEPA Credit Transfer: The Rulebook Layer Above ISO 20022 How the SEPA Credit Transfer scheme constrains ISO 20022: SLEV charges, IBAN-only, the 140-character limit, the Latin character set, and the return flows.
- Payment Message Flows: pain, pacs, and camt End to End How payment messages flow end to end in ISO 20022: pain.001 initiation, pacs.008 interbank, pacs.002 and pain.002 status, and camt reporting. A systems analyst map.
Free account
Practice on the Labs, keep your progress
A free account, no password: an email link signs you in. It saves your steps and self-assessments on the Labs, shows your missions on a dashboard, unlocks the solutions, and, if you tick the box, sends you new missions and articles when they ship.
Your email is used to sign you in. Nothing else, unless you ask. Privacy.