Skip to content

ISO 20022 files (camt.053, pain.001)

Most core banking systems, and Ghana’s interbank systems (GhIPSS Instant Pay and ACH), can produce ISO 20022 XML files. A file drop can load two of them as transactions:

Message What it is Versions read Each transaction is
camt.053 Bank-to-customer statement: what was booked on an account camt.053.001.02 to .001.13 One transaction detail (TxDtls) of an entry, or the entry itself when it has no details
pain.001 Customer credit transfer initiation: payments a customer asked the bank to make pain.001.001.03 to .001.12 One credit transfer instruction (CdtTrfTxInf), as an outgoing, pending transfer

Element names are matched without their namespace, so all those versions read the same way. Only statements (BkToCstmrStmt/Stmt) are read: camt.052 reports and camt.054 notifications are not.

Files are read as a stream, one entry or instruction at a time, so a statement with a million entries needs no more memory than one with ten. Each entry or instruction that can’t be mapped is rejected on its own, with the line where it starts; the rest of the file still loads.

Each entry (Ntry) or instruction (CdtTrfTxInf) may be up to 8 MiB. A larger one is skipped while it is read, never held in memory, and reported on its line as entry 12 is larger than 8 MiB; skipped (or instruction 12 …); the rest of the file still loads. Any other single piece of the file larger than that, such as one huge header or text node, means the file can’t be read: it fails with … an XML element or text larger than …, and nothing from it is loaded.

A payment can appear in a pain.001 (when the customer asks for it) and again in a camt.053 (when it is booked). SpeculaPro gives both the same external_id, its payment id:

  1. The payment’s UETR, when the message carries one. It is unique for every payment.
  2. Otherwise, for an outgoing payment, e2e/<account>/<date>/<EndToEndId>: the end-to-end id, qualified with the paying account (the pain.001’s debtor account, the camt.053’s statement account) and the day (the pain.001’s requested execution date; the camt.053 entry’s value date, else its booking date).

So a payment loaded from both files is recorded once. The second copy differs from the first (pending against booked), so it is reported as external_id was already used for a different transaction in that file’s results, not counted again.

Paths are relative to BkToCstmrStmt/Stmt. Tx means Ntry/NtryDtls/TxDtls, or the entry itself when it has no transaction details. A single-detail entry is one whose NtryDtls holds at most one TxDtls.

Transaction field Taken from
external_id The first that is present: the payment id (Tx/Refs/UETR; for an outgoing DBIT detail, the qualified EndToEndId); Tx/Refs/AcctSvcrRef (the bank’s own reference); for a single-detail entry, Ntry/AcctSvcrRef; Tx/Refs/TxId; the qualified EndToEndId (e2e/<account>/<date>/<EndToEndId>); then Ntry/AcctSvcrRef or Ntry/NtryRef, followed by /<n> (the detail’s position) in a batch entry; finally Stmt/Id + /<entry number>
occurred_at Tx/RltdDts/TxDtTm, else Tx/RltdDts/AccptncDtTm, else Ntry/BookgDt (DtTm or Dt), else Ntry/ValDt. A date or time without a zone is read in your workspace’s time zone; a bare date is midnight
customer_external_id Acct/Ownr/Id: OrgId/Othr/Id, else its LEI or BIC; or PrvtId/Othr/Id. Without an owner id, the account id
customer_type business when the owner id is an OrgId, individual when it is a PrvtId
account_external_id Acct/Id/IBAN, else Acct/Id/Othr/Id
account_type bank
direction CdtDbtInd of the detail, else of the entry: CRDT → in, DBIT → out
amount_minor, currency Tx/AmtDtls/TxAmt/Amt, else Tx/Amt, else (single-detail entries only) Ntry/Amt, with its Ccy attribute
status Ntry/Sts (Cd, or the plain text in older versions): BOOK → completed; PDNG, INFO, FUTR or any other code → pending
type, channel From the bank transaction code, Tx/BkTxCd, else Ntry/BkTxCd: see Bank transaction codes. RvslInd = true makes the type reversal
counterparty Money in: RltdPties/Dbtr (or UltmtDbtr when it has no name), DbtrAcct and RltdAgts/DbtrAgt. Money out: RltdPties/Cdtr (or UltmtCdtr), CdtrAcct and RltdAgts/CdtrAgt
counterparty.name Nm (or Pty/Nm), up to 200 characters
counterparty.identifier The counterparty account’s IBAN (identifier_type iban), else its Othr/Id (account_number)
counterparty.provider The agent’s FinInstnId: BICFI or BIC, else ClrSysMmbId/MmbId (such as a GhIPSS bank sort code), else its other id or name
counterparty.country, counterparty_country PstlAdr/Ctry, else CtryOfRes, else the first two letters of the counterparty’s IBAN
counterparty.type business for an OrgId, individual for a PrvtId
reference Tx/Refs/EndToEndId, else RmtInf/Strd/CdtrRefInf/Ref (140 characters)
description The RmtInf/Ustrd lines joined, else AddtlTxInf, else Ntry/AddtlNtryInf (500 characters)
attributes iso20022_message (camt.053), statement_id, bank_transaction_code, proprietary_code, end_to_end_id, uetr, account_servicer_reference, purpose, value_date, when present

The placeholder NOTPROVIDED, which ISO 20022 uses for a missing end-to-end id, counts as absent everywhere.

An entry is rejected when it has no account id, no amount or currency, a CdtDbtInd other than CRDT or DBIT, no booking or value date, or no reference at all to build an external_id from.

The ISO bank transaction code (domain / family / sub-family, from the ISO 20022 external code list) decides the transaction’s type and channel:

Code type channel
Any sub-family CHRG, FEES, COMM or INTR (charges, fees, commission, interest) fee other
PMNT/RCDT, PMNT/ICDT (credit transfers) transfer bank_transfer; instant_payment for sub-family INST, or a proprietary code containing GIP
PMNT/RDDT, PMNT/IDDT (direct debits) payment bank_transfer
PMNT/CCRD, PMNT/MCRD (cards) payment; sub-family CWDL: withdrawal card; CWDL: atm; POSD, POSP, POSC: pos
PMNT/CNTR (counter transactions) CDPT: deposit; CWDL: withdrawal; else other branch
PMNT/ICHQ, PMNT/RCHQ, PMNT/DRFT (cheques and drafts) payment cheque
Any other PMNT family transfer bank_transfer
LDAS (loans and deposits) DDWN: loan_disbursement; RPMT, PPAY: loan_repayment; else other bank_transfer
CAMT (cash management) transfer bank_transfer
Only a proprietary code, containing GIP (GhIPSS Instant Pay) or INST transfer instant_payment
Only a proprietary code, anything else other other
Any other domain other other
No code at all transfer bank_transfer

The code itself is kept in the bank_transaction_code attribute (for example PMNT/RCDT/INST), and a proprietary code in proprietary_code, so you can see what the bank sent.

Paths are relative to CstmrCdtTrfInitn. Tx means PmtInf/CdtTrfTxInf. A pain.001 is an instruction to pay, not a settled payment, so every transaction is an outgoing transfer with status pending.

Transaction field Taken from
external_id The payment id: Tx/PmtId/UETR, else e2e/<debtor account>/<ReqdExctnDt>/<EndToEndId>. Without either, GrpHdr/MsgId + / + Tx/PmtId/InstrId (else the instruction’s position in the file)
occurred_at GrpHdr/CreDtTm, when the instruction was created. A time without a zone is read in your workspace’s time zone
customer_external_id PmtInf/Dbtr/Id: OrgId/Othr/Id, else its LEI or BIC; or PrvtId/Othr/Id. Without one, the debtor account id
customer_type business for an OrgId, individual for a PrvtId
account_external_id PmtInf/DbtrAcct/Id/IBAN, else DbtrAcct/Id/Othr/Id
account_type bank
direction, type, status out, transfer, pending
channel instant_payment when the service level or local instrument (of the instruction, else of PmtInf) is INST or contains GIP; else bank_transfer
amount_minor, currency Tx/Amt/InstdAmt with its Ccy, else Tx/Amt/EqvtAmt/Amt
counterparty Tx/Cdtr (or UltmtCdtr when it has no name), Tx/CdtrAcct and Tx/CdtrAgt, read as for camt.053
counterparty_country Tx/Cdtr/PstlAdr/Ctry, else CtryOfRes, else the creditor IBAN’s country
reference Tx/PmtId/EndToEndId, else RmtInf/Strd/CdtrRefInf/Ref (140 characters)
description The Tx/RmtInf/Ustrd lines joined (500 characters)
attributes iso20022_message (pain.001), message_id, payment_information_id, instruction_id, end_to_end_id, uetr, purpose, category_purpose, requested_execution_date, service_level, local_instrument, when present

The requested execution date (ReqdExctnDt) may lie in the future, so it is kept as the requested_execution_date attribute rather than used as the transaction’s time.

An instruction is rejected when the file has no GrpHdr/MsgId (and the instruction has no payment id) or no GrpHdr/CreDtTm, when its payment has no debtor account, or when it has no amount or currency.

Every transaction then goes through the same checks as one sent to the API: allowed values, required fields, the duplicate check, conversion to your base currency and encryption of identifiers. Check what loaded in the data source’s File history and in the results file.