Convert legacy SWIFT MT101 requests for transfer to ISO 20022 pain.001 flat records.
Project description
pain001-loader-mt101: MT101 → pain.001 loader
Convert legacy SWIFT MT101 requests for transfer into the flat records
that the pain001 library validates and turns into ISO 20022
pain.001 XML. A single parse_mt101(text) call returns a list[dict]
— one record per transaction — ready to feed straight into pain.001
generation.
Latest release: v0.0.1. The second deliverable of the MT→MX converter project (after
pacs008-loader-mt103). SWIFT MT-MX coexistence ends in November 2025; this loader bridges the window where upstream systems still emit MT101 but downstream tooling expects pain.001.
Contents
- Overview
- Install
- Quick Start
- Field Mapping
- Assumptions and defaults
- Out of scope
- Examples
- Development
- Security
- License
Overview
pain001-loader-mt101 is a small, focused companion to the
pain001 ISO 20022 Customer Credit Transfer Initiation library.
It does one thing well: parse the mandatory + common-denominator MT101
grammar and hand back flat records whose keys are exactly the ones
pain001 validates against the pain.001.001.09 JSON schema. Unlike an
MT103 (one transfer), an MT101 can carry many transactions
(repeating sequence B), so parse_mt101 returns one record per
transaction. The correctness proof is that a realistic multi-transaction
MT101 maps to records that pass
SchemaValidator("pain.001.001.09").validate_batch(...) with zero
errors.
Install
pain001-loader-mt101 requires Python 3.10+ and pulls in pain001
automatically.
pip install pain001-loader-mt101
Quick Start
from pain001_loader_mt101 import parse_mt101
mt101 = """:20:MSGREF2026070901
:21R:CUSTREF-A
:50H:/DE89370400440532013000
GLOBAL IMPORTS GMBH
100 HAFEN STRASSE
HAMBURG
:52A:DEUTDEFF
:30:260712
:21:TXN-REF-0001
:32B:EUR12345,67
:57A:CHASUS33
:59:/GB29NWBK60161331926819
ACME TRADING LTD
1 CORPORATE AVENUE
LONDON
:70:INVOICE 998877
:71A:SHA
:21:TXN-REF-0002
:32B:USD5000,00
:57A:BOFAUS3N
:59:/FR1420041010050500013M02606
LES FLEURS SARL
:70:CONTRACT 445566
:71A:OUR
"""
records = parse_mt101(mt101)
print(len(records)) # 2
print(records[0]["payment_amount"]) # 12345.67
print(records[0]["charge_bearer"]) # SHAR
print(records[0]["nb_of_txs"]) # 2 (message-level)
# Validate against the real pain.001 schema:
from pain001.validation.schema_validator import SchemaValidator
total, valid, errors = SchemaValidator("pain.001.001.09").validate_batch(records)
assert valid == total and not errors
Field Mapping
parse_mt101(text: str) -> list[dict] — one record per sequence-B
transaction. Sequence-A :50a: / :52a: apply to every transaction
unless a sequence-B block overrides them.
| MT101 field | Seq | Meaning | pain.001 key | Notes |
|---|---|---|---|---|
:20: |
A | Sender's Reference | id, payment_information_id |
Message-level |
:30: |
A | Requested Execution Date (YYMMDD) |
requested_execution_date, date |
→ YYYY-MM-DD; SWIFT sliding year window |
:21: |
B | Transaction Reference | payment_id |
One per transaction |
:32B: |
B | Currency + Amount | currency, payment_amount |
No value date; comma-decimal → float |
:50H/50G/50F/50K/50A: |
A/B | Ordering Customer | debtor_name, debtor_account_IBAN, initiator_name |
Name from F (1/), K/H (name lines) or A (BIC); account from /IBAN line |
:52A/52C/52D: |
A/B | Account Servicing Institution | debtor_agent_BIC |
BIC from A/C (or a BIC in D) |
:57A/57C/57D: |
B | Account With Institution | creditor_agent_BIC |
BIC from A/C (or a BIC in D) |
:59/59A/59F: |
B | Beneficiary | creditor_name, creditor_account_IBAN |
Name from plain 59, A (BIC) or F (1/) |
:70: |
B | Remittance Information | remittance_information |
Whitespace-collapsed, capped at 140 chars |
:71A: |
B | Details of Charges | charge_bearer |
OUR→DEBT, BEN→CRED, SHA→SHAR |
| — | — | (synthesised) | nb_of_txs, ctrl_sum |
Count of, and total amount over, sequence-B blocks |
| — | — | (synthesised) | payment_method, batch_booking, service_level_code |
TRF, False, SEPA |
Assumptions and defaults
The pain.001 schema requires fields MT101 does not carry; these are synthesised (override downstream as needed):
payment_method = "TRF"— an MT101 requests credit transfers.batch_booking = False.service_level_code = "SEPA"— the schema enum admits onlySEPA/URNS; override for non-SEPA payments.charge_bearer = "SLEV"when:71A:is absent or unrecognised — the schema requires a charge bearer.remittance_information = "NOTPROVIDED"when:70:is absent — the schema requires a non-empty value.nb_of_txs/ctrl_sumdescribe the whole message (count of and total over sequence-B blocks) and are repeated on every record, as in a pain.001 group header.date(message creation date) reuses:30:— MT101 has no separate creation timestamp. Two-digit years follow the SWIFT sliding window (00–79 → 20YY, 80–99 → 19YY).initiator_namereuses the ordering-customer name.- Absent optional fields are omitted, not guessed. A schema-valid
pain.001 record needs the ordering-customer name + IBAN (
:50a:), the account-servicing and account-with BICs (:52a:/:57a:) and the beneficiary IBAN (:59a:); a well-formed MT101 carries all of these. - Hard requirements — only
:20:,:30:, at least one transaction, and per transaction:21:,:32B:and a named beneficiary (:59:/:59A:/:59F:) raiseValueError. Everything else is best-effort; the loader never crashes on unexpected input.
Out of scope
This is the correct core MT101 → pain.001 mapping, not every optional field. Deliberately excluded in v0.0.1:
:23E:instruction codes,:25:/:28D:authorisation / sequence fields.:33B:instructed currency/amount,:36:exchange rate,:21F:FX deal reference (FX legs).:56a:intermediary and:51A:sending institutions.:77B:regulatory reporting and:25A:charges account.- The SWIFT application header (blocks 1/2). This loader reads block 4
only (it unwraps a
{4:...-}envelope if present).
Examples
Two runnable scripts live in examples/, exercised in CI:
01_minimal_parse.py— parse and print the flat records.02_validate_against_pain001.py— parse, then validate against the realpain.001.001.09schema.
Development
git clone https://github.com/sebastienrousseau/pain001-loader-mt101
cd pain001-loader-mt101
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest # 100% line + branch coverage gate
interrogate pain001_loader_mt101 # 100% docstring gate
mypy pain001_loader_mt101 # strict
Security
pain001-loader-mt101 parses a flat text format with no XML envelope —
the XXE / billion-laughs surface lives upstream. Field regexes are
anchored and bounded, so catastrophic backtracking is not a concern.
See SECURITY.md.
License
Licensed under the Apache License, Version 2.0. Any contribution submitted for inclusion shall be licensed as above, without additional terms.
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 pain001_loader_mt101-0.0.1.tar.gz.
File metadata
- Download URL: pain001_loader_mt101-0.0.1.tar.gz
- Upload date:
- Size: 23.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c26e071e91caef2bece953876d6b16115df3e4164bbea1fa5625eb347803916
|
|
| MD5 |
bd0884bf77964beb17a983248f78ab26
|
|
| BLAKE2b-256 |
5eeec0433173ceaffe1d494b8b44cf64aa9639d143de05af9ccf53f2d7812244
|
Provenance
The following attestation bundles were made for pain001_loader_mt101-0.0.1.tar.gz:
Publisher:
release.yml on sebastienrousseau/pain001-loader-mt101
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pain001_loader_mt101-0.0.1.tar.gz -
Subject digest:
9c26e071e91caef2bece953876d6b16115df3e4164bbea1fa5625eb347803916 - Sigstore transparency entry: 2149823579
- Sigstore integration time:
-
Permalink:
sebastienrousseau/pain001-loader-mt101@c37f04b41354be5fa68ae1eb106c69f41645b1d4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c37f04b41354be5fa68ae1eb106c69f41645b1d4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file pain001_loader_mt101-0.0.1-py3-none-any.whl.
File metadata
- Download URL: pain001_loader_mt101-0.0.1-py3-none-any.whl
- Upload date:
- Size: 16.3 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 |
fefe56f809b3769ef41b3b60ab8490ca0a9e49d80bbc5c392c3e09cfa9b11ca3
|
|
| MD5 |
fb7d82c492849c60ed3e1ef2c4ba7156
|
|
| BLAKE2b-256 |
5a8df9d725729e43e58ea82f9ee7206007b48f5f381970c3c2312c11acf0026b
|
Provenance
The following attestation bundles were made for pain001_loader_mt101-0.0.1-py3-none-any.whl:
Publisher:
release.yml on sebastienrousseau/pain001-loader-mt101
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pain001_loader_mt101-0.0.1-py3-none-any.whl -
Subject digest:
fefe56f809b3769ef41b3b60ab8490ca0a9e49d80bbc5c392c3e09cfa9b11ca3 - Sigstore transparency entry: 2149823751
- Sigstore integration time:
-
Permalink:
sebastienrousseau/pain001-loader-mt101@c37f04b41354be5fa68ae1eb106c69f41645b1d4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sebastienrousseau
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c37f04b41354be5fa68ae1eb106c69f41645b1d4 -
Trigger Event:
workflow_dispatch
-
Statement type: