Before banks talk to each other, a customer talks to their bank. pain.001 is that first instruction, the request a pacs.008 is born from. Here it is field by field, including the identifiers everyone confuses.
Read in the interactive Academy →One tap. In his app, Bob's thumb comes down on "send." That tap has to become something his bank's engine can validate, store, and execute with no human reading it — and it has to carry enough identity that, forty seconds and three banks later, Sweety still sees "Invoice 0042" on her statement.
The pain-family article covered why pain exists and its three-level shape. This is the field-by-field companion to its headline message. And the best way to read a message is to first work out what it's forced to carry.
{{think}} Bob's tap means "pay Sweety ₹33,000." But that alone won't survive the journey. The message has to pass automated validation, possibly sit in a batch of hundreds, get translated into a different message family, and still let Sweety's bank print the right reference on her statement three banks later.
Beyond the amount and the payee, what is this message forced to carry to make all that work? {{reveal}} Three things fall out:
And note what it does not carry: money. A pain.001 is a request. The money moves later, in the pacs.008 the bank builds from it. {{/think}}
A pain.001 is a file with the three-block skeleton from anatomy of a message:
GrpHdr) — file-wide facts, once: message id, timestamp, total transaction count, control sum.PmtInf) — one block per shared set: the debtor (Bob), his account, the debit date, the method. A file may hold many.CdtTrfTxInf) — one block per payee: Sweety, her account, the amount, the reference.Bob's app sends one of each. A payroll sends one GrpHdr, one PmtInf, four hundred CdtTrfTxInf blocks. Same message, same shape.
{{think}} A big corporate rarely banks with one institution — it might initiate payments through five. If each of those five banks reads pain.001 its own way, expects fields in its own optional slots, and rejects on its own quirks, what does the corporate end up maintaining? And what would fix it? {{reveal}} Five subtly different pain.001 builders for what's supposedly one standard — the exact "we're all ISO 20022, so why doesn't it just work?" trap.
The fix is CGI-MP (Common Global Implementation – Market Practice): a usage guideline, not a new message, agreed by banks, corporates, and vendors on a common subset of pain.001 (and its siblings). Build to CGI-MP once and it works, largely unchanged, across every aligned bank. It's the corporate-to-bank cousin of CBPR+ and HVPS+: same idea, different leg of the journey. Just keep it honest — it's market practice, not law, so it narrows the surprises rather than erasing them. You still confirm each bank's specifics. {{/think}}
A pain.001 carries several ids at different levels, doing different jobs, plus the UETR alongside:
MsgId (in GrpHdr) — the file/message. The bank uses it to catch a duplicate file.PmtInfId (in PmtInf) — the batch (this debit run).InstrId (in CdtTrfTxInf) — the instruction id, meaningful between you and your bank only. Not guaranteed to travel onward.EndToEndId (in CdtTrfTxInf) — your reference, which must survive every hop untouched to Sweety. This is how "Invoice 0042" reaches her statement.UETR — a globally unique UUIDv4 tracking the payment across every bank, fixed for its whole life.Rule of thumb: InstrId is local and disposable; EndToEndId and UETR are sacred and end-to-end.
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>BOBAPP-20260701-0001</MsgId> <!-- the file's id -->
<CreDtTm>2026-07-01T08:15:00+04:00</CreDtTm>
<NbOfTxs>1</NbOfTxs> <!-- must match the count below -->
<CtrlSum>33000.00</CtrlSum> <!-- must equal the sum of amounts -->
<InitgPty><Nm>Bob Marsh</Nm></InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>BOB-RUN-0042</PmtInfId> <!-- the batch id -->
<PmtMtd>TRF</PmtMtd>
<ReqdExctnDt>2026-07-01</ReqdExctnDt>
<Dbtr><Nm>Bob Marsh</Nm></Dbtr>
<DbtrAcct><Id><IBAN>AE070331234567890123456</IBAN></Id></DbtrAcct>
<DbtrAgt><FinInstnId><BICFI>EBILAEAD</BICFI></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId>
<InstrId>BOB-INSTR-1</InstrId> <!-- local, may not travel -->
<EndToEndId>BOB-INV0042</EndToEndId> <!-- travels untouched -->
<UETR>eb6305c9-1f7c-4a9b-9b1e-2c2f4e7a91d4</UETR>
</PmtId>
<Amt><InstdAmt Ccy="INR">33000.00</InstdAmt></Amt>
<Cdtr><Nm>Sweety Rao</Nm></Cdtr>
<CdtrAcct><Id><IBAN>IN80HDFC0001234567890123</IBAN></Id></CdtrAcct>
<CdtrAgt><FinInstnId><BICFI>HDFCINBB</BICFI></FinInstnId></CdtrAgt>
<RmtInf><Ustrd>Invoice 0042 — June freelance</Ustrd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
Two validations bite before anything else: NbOfTxs must equal the number of CdtTrfTxInf blocks, and CtrlSum must equal the sum of their amounts. Get either wrong and the whole file is rejected — self-checking by design.
Open pain.001 in the Message Explorer →
Bob's bank replies not with money but a pain.002 status report: Accepted (ACCP/ACSP), Rejected (RJCT, with a reason code like AC01 incorrect account or AM04 insufficient funds, pinpointing which transaction and why), or Pending (PDNG). This is the tick or error in Bob's app — a receipt about the request, not the payment.
The full status vocabulary behind that tick: pain.002 & pacs.002 →
{{aside:model|The mental model}} pain.001 = you instructing your bank; no money moves inside it. It's a request the pacs.008 is born from. Of its references, EndToEndId and UETR are sacred (survive untouched, end to end); InstrId is local and disposable; NbOfTxs/CtrlSum make the file self-checking. {{/aside}}
{{aside:chair|From the engineer's chair}} When your bank turns the pain.001 into a pacs.008 it keeps EndToEndId and UETR untouched, adds the interbank settlement fields (IntrBkSttlmAmt, IntrBkSttlmDt, settlement method, the agent chain), and drops the purely local InstrId and the GrpHdr/CtrlSum framing that was only ever between you and your bank. That keep/add/drop is the pain → pacs seam. {{/aside}}
{{aside:breaks|Where it breaks}}
{{/aside}}
CtrlSum ≠ the sum of amounts (or NbOfTxs ≠ the block count) rejects the whole file.InstrId downstream. It's local; it may not travel. If you need a reference to reach the creditor, that's EndToEndId's job.EndToEndId. NOTPROVIDED or empty means the customer's thread is broken before it starts — reconciliation dies at the far end.{{aside:map|The map}} Where pain.001 sits in the shelf:
{{/aside}}
{{aside:ref|Reference card}}
{{/aside}}
NbOfTxs = block count, CtrlSum = sum of amounts, or the file is rejected.See the pacs.008 your bank builds from this, in the Explorer →
You can read a pain.001 field by field; keep MsgId (the file), PmtInfId (the batch), InstrId (local, disposable), EndToEndId (sacred, end-to-end) and the UETR straight; explain why NbOfTxs and CtrlSum make the file self-checking; read a pain.002 and its reason codes; and name exactly what your bank keeps, adds, and drops when it turns your pain.001 into a pacs.008.
What are the three levels of a pain.001? Group header, payment information, and individual transaction detail
The declared control sum doesn't match the transactions inside. What happens? The whole file is typically rejected
What is CGI-MP, and what problem does it solve? A corporate-to-bank usage guideline that standardises how pain.001 is used, so a multi-banked corporate can maintain one implementation across banks