Hex.pm Docs CI License: MIT

ISO 20022 message parsing for Elixir. Currently covers camt.053 (Bank to Customer Statement), the highest-demand message type. More message types are planned.

Installation

# mix.exs
def deps do
  [
    {:ex_iso20022, "~> 0.1"}
  ]
end

Quick start

xml = File.read!("statement.xml")

case ISO20022.Camt053.parse(xml) do
  {:ok, doc} ->
    Enum.each(doc.statements, fn stmt ->
      IO.puts("Account IBAN : #{stmt.account.iban}")
      IO.puts("Currency     : #{stmt.account.currency}")

      closing = Enum.find(stmt.balances, &(&1.type == :closing_booked))
      IO.puts("Closing bal  : #{closing.amount} #{closing.currency} (#{closing.credit_debit})")

      Enum.each(stmt.entries, fn entry ->
        IO.puts("  #{entry.ref}  #{entry.credit_debit}  #{entry.amount} #{entry.currency}")
      end)
    end)

  {:error, reason} ->
    IO.inspect(reason, label: "parse error")
end

Using the bang variant when you are confident the input is valid:

doc = ISO20022.Camt053.parse!(xml)

Supported message types

ModuleMessageVersions
ISO20022.Camt053Bank to Customer Statementcamt.053.001.02 – 014

More message types (camt.052, camt.054, pain.001, pacs.008, …) will be added in subsequent releases. The top-level ISO20022.parse/1 dispatcher is already in place and will route to the right module automatically once each type is implemented.

Struct reference

ISO20022.Camt053.Document

The top-level struct returned by parse/1.

FieldTypeDescription
group_headerGroupHeaderMessage-level metadata
statements[Statement]One entry per account per period

ISO20022.Camt053.GroupHeader

FieldTypeDescription
message_idStringUnique message identifier (max 35 chars)
created_atDateTimeUTC creation timestamp
paginationmap | nil%{page_number: String, last_page: boolean}

ISO20022.Camt053.Statement

FieldTypeDescription
idStringStatement identifier
electronic_seq_numberinteger | nilSequence number for gap detection
created_atDateTime | nilStatement generation time
from_to_datemap | nil%{from: DateTime, to: DateTime}
accountAccountAccount identification
balances[Balance]At least opening and closing booked balances
entries[Entry]Booked transactions

ISO20022.Camt053.Account

FieldTypeNotes
ibanString | nilPresent when account is IBAN-identified
other_idString | nilPresent when account uses a non-IBAN scheme
other_schemeString | nilScheme code, e.g. "BBAN"
currencyString | nilISO 4217 alpha code, e.g. "EUR"
servicer_bicString | nilBIC of the account-servicing institution
nameString | nilAccount name

ISO20022.Camt053.Balance

amount is always a positive Decimal. The sign is expressed through credit_debit.

FieldTypeDescription
typeatomSee balance types below
amountDecimalPositive amount
currencyStringISO 4217 alpha code
credit_debit:credit | :debit:debit means overdraft
dateDateBalance reference date

Balance type atoms:

AtomISO codeMeaning
:opening_bookedOPBDOpening booked (mandatory)
:closing_bookedCLBDClosing booked (mandatory)
:closing_availableCLAVClosing available
:interim_bookedITBDInterim booked
:interim_availableITAVInterim available
:forward_availableFWAVForward available
{:other, code}anyUnrecognised code

ISO20022.Camt053.Entry

FieldTypeDescription
refStringBank-assigned entry reference
amountDecimalPositive amount
currencyStringISO 4217 alpha code
credit_debit:credit | :debitDirection
reversalbooleantrue if this cancels a prior entry
status:bookedAlways :booked in camt.053
booking_dateDate | nilDate posted to account
value_dateDate | nilValue date (may differ from booking date)
account_servicer_refString | nilBank's own reference
bank_transaction_codeBankTxCode | nilISO 20022 transaction classification
additional_infoString | nilFree-text entry description
details[EntryDetails]Transaction-level detail blocks (batch entries)

ISO20022.Camt053.BankTxCode

FieldTypeExample
domainString | nil"PMNT"
familyString | nil"RCDT", "ICDT", "RDDT"
sub_familyString | nil"XBCT", "ESCT", "SALA"
proprietary_codeString | nilBank-specific code
proprietary_issuerString | nilIssuer of the proprietary code

ISO20022.Camt053.EntryDetails

Present when an entry groups multiple underlying transactions (batch payments).

FieldTypeDescription
batchmap | nil%{message_id, payment_info_id, number_of_transactions, total_amount}
transaction_details[TransactionDetails]Individual transaction records

ISO20022.Camt053.TransactionDetails

FieldTypeDescription
refsmap | nil%{message_id, end_to_end_id, uetr, …}
amountDecimal | nilIndividual transaction amount
currencyString | nilISO 4217
credit_debit:credit | :debit | nilDirection
related_partiesmap | nil%{debtor, creditor, ultimate_debtor, ultimate_creditor}
related_agentsmap | nil%{debtor_agent, creditor_agent}
remittance_infotuple / nil{:unstructured, text} or {:structured, %{ref, ref_type, …}}
purposeString | nilISO 20022 purpose code

Error handling

{:ok, %ISO20022.Camt053.Document{}}

# Malformed XML
{:error, {:parse_error, reason}}

# Namespace not recognised as a camt.053 variant
{:error, {:unsupported_version, "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"}}

# Mandatory field absent from an otherwise valid document
{:error, {:missing_required_field, [:group_header, :message_id]}}
{:error, {:missing_required_field, [:statements, 0, :id]}}

# Field present but value could not be parsed
{:error, {:invalid_amount, "N/A", [:statements, 0, :entries, 1, :amount]}}
{:error, {:invalid_date, "32-01-2024"}}

The path in missing_required_field and invalid_* errors follows the struct hierarchy so it is straightforward to pinpoint the problematic node.

Multi-version support

Real-world bank files use a wide range of schema versions:

urn:iso:std:iso:20022:tech:xsd:camt.053.001.02   # many European banks
urn:iso:std:iso:20022:tech:xsd:camt.053.001.04   # UK, SEPA migration era
urn:iso:std:iso:20022:tech:xsd:camt.053.001.08   # current SWIFT / TARGET2
urn:iso:std:iso:20022:tech:xsd:camt.053.001.11   # newer implementations
urn:iso:std:iso:20022:tech:xsd:camt.053.001.14   # latest ISO (2026)

ex_iso20022 detects the version from the root element's xmlns attribute and normalises to the same struct regardless of input version. All versions 02 – 14 are accepted. Documents lacking a namespace (some older senders) are also accepted.

License

MIT