Camt053 is a Python library for reading ISO 20022 camt Bank-to-Customer Statements (camt.053/052/054) and generating validated reversing entries by return reason code (e.g. AC04 Closed Account).
Project description
camt053: ISO 20022 Bank Statements and Reversing Entries
Read ISO 20022 camt Bank-to-Customer Cash Management messages, extract
booked entries by return reason code (e.g. AC04 Closed Account), and generate
validated reversing entries — the core of a modern, AI-assisted treasury
stack with native MCP and LSP integrations.
Latest release: v0.0.5 — namespace-agnostic camt.052/053/054 parsing and one-shot reversing-entry generation, validated against the official ISO 20022
camt.053.001.14schema, for Python 3.10+. See what's new →
Contents
- Overview
- Install
- Quick Start
- Features
- Usage
- Supported messages
- Architecture
- Examples
- The camt053 suite
- When not to use camt053
- Development
- Security
- Documentation
- License
- Contributing
- Acknowledgements
Overview
camt053 reads ISO 20022 camt cash-management messages — the standardised
bank-to-customer statements (camt.053), account reports (camt.052), and
debit/credit notifications (camt.054) — into a typed model, lets you filter
booked entries by ISO external return reason code, and generates a validated
reversing entry for the matching transactions.
The headline capability is the one-shot reversing-entry workflow: read an incoming camt.053 statement, find the entries carrying a return reason code (e.g. AC04 Closed Account), and emit a validated reversing entry — answering the prompt-engineering dream:
"Read this incoming bank statement XML, parse out the transactions with error code AC04, and automatically generate the reversing entry."
- Documentation: https://sebastienrousseau.github.io/camt053/
- Source code: https://github.com/sebastienrousseau/camt053
- Bug reports: https://github.com/sebastienrousseau/camt053/issues
A single shared facade (camt053.services) backs four developer surfaces — the
Python API, the CLI, the REST API, and the companion MCP and LSP servers — so
every interface behaves identically. This package is part of the camt053
suite (all Python 3.10+):
| Package | Role |
|---|---|
camt053 |
Core library + Click CLI + FastAPI REST API (this package) |
camt053-mcp |
Model Context Protocol server (for AI agents) |
camt053-lsp |
Language Server Protocol server (for editors) |
flowchart LR
A["Inbound camt.05x XML"] -->|parse| B["camt053.services"]
B -->|filter by reason code| C["AC04 entries"]
C -->|reverse + validate| D["camt.053.001.14 reversing entry"]
Install
camt053 runs on macOS, Linux, and Windows and requires Python 3.10+ and pip.
python -m pip install camt053
Using an isolated virtual environment (recommended)
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows
python -m pip install -U camt053
Quick Start
from camt053 import services
# An incoming camt.053 statement (truncated for brevity — see examples/).
statement_xml = open("statement.xml", encoding="utf-8").read()
# Find the entries returned AC04 (Closed Account).
ac04 = services.filter_entries(statement_xml, "AC04")
print(f"{len(ac04)} AC04 entr{'y' if len(ac04) == 1 else 'ies'}")
# Generate the reversing entry: parse -> filter -> reverse, in one call.
reversal_xml = services.generate_reversal(statement_xml, reason_code="AC04")
print(reversal_xml) # validated camt.053.001.14 document
Or from the command line:
# Generate a reversing entry for every AC04 entry on a statement
camt053 reverse -i statement.xml -r AC04 -o reversal.xml
# List the entries on a statement (filter by reason, status, date, or amount)
camt053 entries -i statement.xml -r AC04
camt053 entries -i statement.xml --status BOOK --from 2026-06-01 --min 1000
# Export the (filtered) entries as CSV or JSON, to stdout or a file
camt053 entries -i statement.xml --export csv -o entries.csv
camt053 entries -i statement.xml -r AC04 --export json
# Choose the output format: a Rich table (default) or structured JSON
camt053 entries -i statement.xml --format json
camt053 reverse -i statement.xml -r AC04 --format json # JSON envelope
# Inspect the parsed statement as JSON, or validate an identifier
camt053 parse -i statement.xml
camt053 validate-id -k iban -v GB29NWBK60161331926819
# Validate an incoming statement against its official ISO camt XSD
camt053 validate -i statement.xml
parse, entries, reverse, and validate accept -i - to read from stdin,
so they compose in a pipeline.
Features
- Parse camt.053 / camt.052 / camt.054 into a typed, JSON-serialisable
model. Parsing is namespace-agnostic, so every ISO version (
.001.01through.001.14) and real-world bank file is read. - Filter booked entries by ISO external return reason code (AC04, AC06,
MD07, …), and by status, booking-date range, and amount range
(all ANDed) via
services.filter_entries(...)or thecamt053 entriesflags. - Return reason codes — a substantial slice of the ISO 20022
ExternalReturnReason1Codeset (the common SEPA / CBPR+ return reasons), listed viacamt053 reasons, with case-insensitive lookup throughservices.validate_reason_code(code) -> {"code", "name", "valid"}. - Reason-code action policy — classify a return reason into a handling
action (
"return","retry", or"ignore") viaservices.classify_reason(code) -> {"code", "name", "action"}, with a sensible built-in default (account-level rejections return, transient conditions such asAM04/AM05retry, informational reasons ignore). The full mapping isservices.reason_policy(); both accept anoverridesmapping and a customdefault. Thecamt053 reasonstable shows the action column andcamt053 classify -r AC04classifies a single code. - Export the (filtered) entries to CSV or JSON (
camt053 entries --export {csv,json} [-o file]); CSV columns arereference, amount, currency, credit_debit_indicator, status, booking_date, value_date, reason_code. - Structured output —
camt053 entries --format jsonemits the entries as a JSON array, andcamt053 reverse --format jsonemits a{"message_type", "reason_code", "xml"}envelope instead of raw XML (--format table, the default, keeps the Rich table / raw XML). - Reverse — generate a
camt.053.001.14reversing entry from the matching entries (credit/debit indicator flipped,RvslIndset, return reason carried inRtrInf), in one call. - Validated output — generated reversals are checked against the official
ISO 20022
camt.053.001.14XSD bundled with the package. - SWIFT charset cleansing — opt-in cleansing of the name / narrative fields
(
Nm/AddtlInf/ party / counterparty names) bound for SWIFT FIN / CBPR+ rails: characters outside the SWIFT X set are transliterated (é→e,ß→ss, smart quotes / dashes folded) or stripped, and field maximum lengths are enforced. Enable it on the reversal path withservices.generate_reversal(xml, cleanse=True)/services.generate(records, cleanse=True)(default off, so existing output is unchanged), or cleanse records directly withservices.cleanse_records(records) -> {"changed", "fields": [report, ...]}, which returns an audit report of exactly what changed. Cleansed reversals still validate against the bundled XSD. - Validate incoming statements —
services.validate_statement(xml)(and thecamt053 validatecommand) check an inbound camt.052 / camt.053 / camt.054 document against the matching official ISO 20022 XSD, detected from its namespace, returning{"valid", "message_type", "errors"}. - Re-serialise (round-trip) — render a parsed
ParsedDocument/Statementback to a validatedcamt.053.001.14document viaservices.serialize_statement(xml)(orcamt053.serialize_document(doc)/camt053.serialize_statement(stmt)). The output is deterministic and round-trip stable:parse_document(serialize_statement(parse_document(xml)))preserves the account, balances, and entries (references, amounts, currencies, credit/debit indicators, and return reasons). - Safe by default — XML is parsed with
defusedxml(XXE / billion-laughs safe); output paths are traversal-checked. - One facade, four interfaces — the CLI, REST API, MCP server, and LSP
server all call
camt053.services. - IBAN / BIC / LEI validators (ISO 13616 / 9362 / 17442).
- Decimal amounts & ISO 4217 currencies —
Entry.amount_decimal/Balance.amount_decimalparse the string amount into aDecimal(the string is kept verbatim for XML fidelity), andservices.validate_currency(code) -> {"code", "valid", "minor_units"}checks a code against a bundled ISO 4217 set and reports its minor units (EUR=2, JPY=0, …). - Typed (mypy
--strict) and tested (100% coverage), validated against the official ISO 20022 business samples.
Usage
from camt053 import parse_statement, services
# A minimal incoming camt.053 statement: a EUR 1,500 credit transfer that was
# booked, then returned because the beneficiary account was closed (AC04).
statement_xml = """<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.14">
<BkToCstmrStmt>
<GrpHdr><MsgId>STMT-MSG-0001</MsgId><CreDtTm>2026-06-15T08:00:00</CreDtTm></GrpHdr>
<Stmt>
<Id>STMT-0001</Id><CreDtTm>2026-06-15T08:00:00</CreDtTm>
<Acct><Id><IBAN>GB29NWBK60161331926819</IBAN></Id><Ccy>EUR</Ccy></Acct>
<Bal><Tp><CdOrPrtry><Cd>CLBD</Cd></CdOrPrtry></Tp>
<Amt Ccy="EUR">10000.00</Amt><CdtDbtInd>CRDT</CdtDbtInd>
<Dt><Dt>2026-06-15</Dt></Dt></Bal>
<Ntry>
<NtryRef>NTRY-0001</NtryRef>
<Amt Ccy="EUR">1500.00</Amt><CdtDbtInd>CRDT</CdtDbtInd>
<Sts><Cd>BOOK</Cd></Sts>
<NtryDtls><TxDtls>
<RtrInf><Rsn><Cd>AC04</Cd></Rsn></RtrInf>
</TxDtls></NtryDtls>
</Ntry>
</Stmt>
</BkToCstmrStmt>
</Document>"""
# 1. Parse into the typed model.
statement = parse_statement(statement_xml)
print(statement.account.identifier()) # -> GB29NWBK60161331926819
print(len(statement.entries)) # -> 1
# 2. Select the entries returned AC04 (Closed Account).
ac04 = statement.entries_with_reason("AC04")
print(ac04[0].amount, ac04[0].credit_debit_indicator) # -> 1500.00 CRDT
# 3. Generate the validated reversing entry (the original CRDT becomes DBIT).
reversal_xml = services.generate_reversal(statement_xml, reason_code="AC04")
assert "<RvslInd>true</RvslInd>" in reversal_xml
assert "<CdtDbtInd>DBIT</CdtDbtInd>" in reversal_xml
Supported messages
| Message type | Name | Direction |
|---|---|---|
camt.052.001.14 |
Bank To Customer Account Report | read |
camt.053.001.14 |
Bank To Customer Statement | read + reverse |
camt.054.001.14 |
Bank To Customer Debit Credit Notification | read |
The parser is namespace-agnostic and reads every ISO version of these messages;
the official XSDs for .001.01–.001.14 are bundled under camt053/xsd/.
Reversing entries are emitted as camt.053.001.14.
Architecture
flowchart TD
CLI["Click CLI"] --> S["camt053.services"]
API["FastAPI REST API"] --> S
MCP["camt053-mcp"] --> S
LSP["camt053-lsp"] --> S
S --> P["parse/ — statement_parser, reason_codes"]
S --> R["reversal/ — reversal builder"]
S --> X["xml/ — template + official ISO XSD"]
S --> V["validation/ — IBAN, BIC, LEI, JSON Schema"]
| Module | Responsibility |
|---|---|
camt053.parse |
Namespace-agnostic statement parser and return-reason helpers |
camt053.reversal |
Builds flat reversing-entry records from parsed entries |
camt053.xml |
Renders the camt.053 reversal template and validates it via the ISO XSD |
camt053.validation |
IBAN / BIC / LEI and JSON-Schema validators |
camt053.security |
XXE-safe parsing and path-traversal-checked output |
camt053.services |
The shared facade backing every interface |
Error handling
Every exception in camt053.exceptions inherits from
Camt053Error and carries a stable, machine-readable code. These codes are
part of the public API — they are guaranteed unique and will not change across
releases — so you can switch on exc.code (e.g. to map a failure onto an HTTP
status) without depending on the class name or message text.
| Code | Exception | Meaning |
|---|---|---|
CAMT053_ERROR |
Camt053Error |
Base error for any Camt053 failure |
ACCOUNT_VALIDATION_ERROR |
AccountValidationError |
Account/input data failed validation |
XML_GENERATION_ERROR |
XMLGenerationError |
XML rendering or template failure |
CONFIGURATION_ERROR |
ConfigurationError |
Invalid configuration or CLI arguments |
DATA_SOURCE_ERROR |
DataSourceError |
A data source could not be read |
SCHEMA_VALIDATION_ERROR |
SchemaValidationError |
XML did not conform to its ISO 20022 XSD |
INVALID_IBAN_ERROR |
InvalidIBANError |
IBAN format / checksum validation failed |
INVALID_BIC_ERROR |
InvalidBICError |
BIC/SWIFT format validation failed |
INVALID_LEI_ERROR |
InvalidLEIError |
LEI format / checksum validation failed |
MISSING_REQUIRED_FIELD_ERROR |
MissingRequiredFieldError |
A required field was absent |
STATEMENT_PARSE_ERROR |
StatementParseError |
An incoming statement could not be parsed |
REVERSAL_GENERATION_ERROR |
ReversalGenerationError |
A reversing entry could not be generated |
from camt053 import services
from camt053.exceptions import Camt053Error
try:
services.generate_reversal(statement_xml, reason_code="AC04")
except Camt053Error as exc:
log.error("[%s] %s", exc.code, exc)
Robustness
The statement parser is built for the messy reality of inbound bank files: malformed-but-recoverable statements degrade gracefully rather than failing outright.
- Missing optional elements (owner name, currency, balances, booking date,
return reason, ...) read as
None/ empty — only the<Document>envelope wrapping a recognised camt.05x container is mandatory. - Unknown or extra elements (vendor extensions, unexpected siblings) are ignored: children are matched by local name, not by a fixed schema.
- Unexpected namespaces and prefixes are tolerated — a prefixed
<camt:Document>root, a missing namespace, or a non-ISO namespace URI all parse the same way.
Genuinely non-well-formed XML (unclosed / mismatched tags, bad entities) still
raises StatementParseError, which carries the 1-based source line (and
column, where reported) so the offending byte can be located. See
camt053/parse/statement_parser.py for
the documented recovery limits.
Examples
Runnable, self-contained scripts live in examples/:
| Example | Demonstrates |
|---|---|
reverse_ac04.py |
The headline workflow — find AC04 entries and generate the reversing entry |
parse_statement.py |
Parsing a statement into the typed model |
services_facade.py |
The shared camt053.services facade |
validate_identifiers.py |
IBAN / BIC / LEI validation |
rest_api_client.py |
Driving the FastAPI REST API in-process |
git clone https://github.com/sebastienrousseau/camt053.git && cd camt053
python examples/reverse_ac04.py
The camt053 suite
camt053 is the core of a set of independently installable packages
— pick whichever ones your stack needs:
| Package | Role |
|---|---|
camt053 |
Core library + CLI + FastAPI REST API (this package) |
camt053-mcp |
Model Context Protocol server (for AI agents) |
camt053-lsp |
Language Server Protocol server (for editors) |
camt053-writer-xlsx |
Excel .xlsx writer for parsed statements |
camt053-loader-mt940 |
SWIFT MT940 → camt.053 loader |
The MCP, LSP, writer-xlsx, and loader-mt940 packages are thin wrappers
over the shared camt053.services facade exported here, so every
interface behaves identically.
When not to use camt053
- You need to generate payment files (pain.001 Customer Credit
Transfer, pain.008 Direct Debit). Use
pain001— the sibling library for outbound ISO 20022. - You need to parse pacs.* (FI-to-FI) messages. Out of scope; this library targets the camt.05x (Bank-to-Customer) family only.
- You need bank API transport (PSD2, EBICS, SWIFTNet). camt053 reads files and emits files; it does not move them. Pair with a dedicated transport library.
- You need a GUI. This is a library + CLI + REST API; the closest thing to a UI is the LSP server's in-editor diagnostics.
- You need real-time processing (FedNow / TIPS / RTP). Those rails use camt.052 intraday reporting, not camt.053 end-of-day statements; the parser accepts camt.052 but the reversing-entry workflow is camt.053-shaped.
Development
git clone https://github.com/sebastienrousseau/camt053.git && cd camt053
mise install
poetry install
poetry shell
A Makefile orchestrates the quality gates (kept in lockstep with CI):
make check # all gates (REQUIRED before commit)
make test # pytest with coverage (100% gate)
make lint # ruff + black --check
make type-check # mypy --strict
make examples # run the example scripts
Security
camt053 is the entry point for untrusted XML from banks for
every consumer in the suite. Defence-in-depth is taken seriously:
parsing uses defusedxml (XXE / billion-laughs neutralised), a
pre-flight xml_guard enforces a configurable byte cap and refuses
inline DOCTYPE / ENTITY declarations, and the REST API additionally
caps request bodies via middleware. Reporting practice, supported
versions, fix-window SLAs, and the full supply-chain posture (PyPI
Trusted Publishing, sigstore attestations, signed tags) live in
SECURITY.md. Vulnerabilities go via GitHub Private
Vulnerability Reporting, not public issues.
News / Releases
- 2026-06-22 — Shipping camt053 v0.0.6 for the 14-16 November 2026 ISO 20022 cliff (v0.0.6 release announcement + Nov 2026 cliff narrative).
Documentation
README.md— this fileCHANGELOG.md— release notesROADMAP.md— milestones and the explicit Declined/Deferred listGOVERNANCE.md— decision model + becoming a maintainerSECURITY.md— disclosure + supported versions + supply chain + NIST SSDF mappingSUPPORT.md— how to get help, by needMAINTAINERS.md— who can merge and cut releasesCONTRIBUTING.md— submission process + styleSTYLEGUIDE.md— cross-suite style guide (linked from every package README)docs/version-matrix.md— supportedcamt.05xrevisions with the Nov 2026 cliffexamples/— runnable scripts, exercised in CIdocs/— extended reference (API, quickstart, deployment cookbook)- Hosted Sphinx docs: https://sebastienrousseau.github.io/camt053/
License
Licensed under the Apache License, Version 2.0. Any contribution submitted for inclusion shall be licensed as above, without additional terms.
Contributing
Contributions are welcome — see the contributing instructions. Thanks to all contributors.
Acknowledgements
Built on Click,
Rich, Jinja2,
xmlschema,
defusedxml, and
FastAPI, against the official ISO 20022
camt.05x schemas.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file camt053-0.0.7.tar.gz.
File metadata
- Download URL: camt053-0.0.7.tar.gz
- Upload date:
- Size: 457.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1414c1ea7690a47dc82763202689f124cd5953946a3740765b7e94ad581f480e
|
|
| MD5 |
646ec2126221b38881fa3d25bfc17cef
|
|
| BLAKE2b-256 |
2f5d59591e024e08e0f2b8c1e484dce5d9c08f01209b6be88948defbed719a7c
|
Provenance
The following attestation bundles were made for camt053-0.0.7.tar.gz:
Publisher:
release.yml on sebastienrousseau/camt053
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
camt053-0.0.7.tar.gz -
Subject digest:
1414c1ea7690a47dc82763202689f124cd5953946a3740765b7e94ad581f480e - Sigstore transparency entry: 1967270361
- Sigstore integration time:
-
Permalink:
sebastienrousseau/camt053@6be84725b0e007cfbc69868956fa49378611aad9 -
Branch / Tag:
refs/tags/v0.0.7 - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6be84725b0e007cfbc69868956fa49378611aad9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file camt053-0.0.7-py3-none-any.whl.
File metadata
- Download URL: camt053-0.0.7-py3-none-any.whl
- Upload date:
- Size: 531.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50ef4b71fdfcdacc04da429a9f2f1f643c87c992d714b383f84630f742889cb7
|
|
| MD5 |
04d5db337cd34577603868f0aee23bdf
|
|
| BLAKE2b-256 |
fadd47f8c47cff666ddd96d3529df72224422657af50f5a3f68929aa72b723e7
|
Provenance
The following attestation bundles were made for camt053-0.0.7-py3-none-any.whl:
Publisher:
release.yml on sebastienrousseau/camt053
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
camt053-0.0.7-py3-none-any.whl -
Subject digest:
50ef4b71fdfcdacc04da429a9f2f1f643c87c992d714b383f84630f742889cb7 - Sigstore transparency entry: 1967270458
- Sigstore integration time:
-
Permalink:
sebastienrousseau/camt053@6be84725b0e007cfbc69868956fa49378611aad9 -
Branch / Tag:
refs/tags/v0.0.7 - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6be84725b0e007cfbc69868956fa49378611aad9 -
Trigger Event:
push
-
Statement type: