Guides · Errors and rejections · By Tan Gravam · Updated 2026-10-20
Validate a pain.001 against the XSD on your own machine
Get the XSD of your file’s version from iso20022.org and run xmllint --noout --schema pain.001.001.09.xsd payments.xml: it prints "payments.xml validates", or each error with its line number and "fails to validate". Python (lxml) and Java (javax.xml.validation) do the same in code. A schema-valid file can still break SEPA rules, because the schema does not contain them.
Get the official schema
The ISO 20022 schemas are published by the ISO 20022 Registration Authority on iso20022.org. The copies BankLint uses for pain.001.001.09 and pain.001.001.03 come from its message archive (Payments Initiation message set) and are used unmodified. Validate against the version your file declares, and keep the schema as published: an edited copy no longer checks the standard.
| Version | Namespace (`xmlns` of `Document`) | Content of `Document` |
|---|---|---|
pain.001.001.09 | urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 | CstmrCdtTrfInitn of type CustomerCreditTransferInitiationV09 |
pain.001.001.03 | urn:iso:std:iso:20022:tech:xsd:pain.001.001.03 | CstmrCdtTrfInitn of type CustomerCreditTransferInitiationV03 |
Each schema declares only Document in its own namespace. A file in another namespace, such as a .03 file checked against the .09 schema, fails at its root element: xmllint and lxml report "No matching global declaration available for the validation root", Java reports cvc-elt.1.a. Check the namespace before reading any other error.
xmllint on the command line
xmllint is the command-line tool of libxml2, the library BankLint’s validator also uses (through lxml). --noout stops it from printing the document itself.
xmllint --noout --schema pain.001.001.09.xsd payments.xmlpayments.xml validatespayments.xml:134: element IBAN: Schemas validity error : Element '{urn:iso:std:iso:20022:tech:xsd:pain.001.001.09}IBAN': [facet 'pattern'] The value 'FR00 NOT AN IBAN' is not accepted by the pattern '[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}'.
payments.xml fails to validateThe exit status is 0 for a valid file and 3 for a schema error, so the command can gate a script or a CI job. Try it with the synthetic file with an invalid IBAN pattern.
Python with lxml
lxml uses libxml2, so the messages are the ones xmllint prints. In tests, schema.assertValid(doc) raises etree.DocumentInvalid with the first error instead. The parser settings stop a file from reaching the network, expanding entities or loading a DTD; BankLint parses files with the same three settings.
import sys
from lxml import etree
# No network access, no entity expansion, no DTD loading
parser = etree.XMLParser(no_network=True, resolve_entities=False, load_dtd=False)
schema = etree.XMLSchema(etree.parse("pain.001.001.09.xsd", parser))
doc = etree.parse(sys.argv[1], parser)
if schema.validate(doc):
print("valid")
else:
for error in schema.error_log:
print(f"line {error.line}: {error.message}")Java with javax.xml.validation
The JDK validator starts each message with the W3C XML Schema constraint that failed, such as cvc-pattern-valid or cvc-complex-type.2.4.a. Without an error handler it stops at the first error. Each code is explained, with failing and corrected XML, in pain.001 XSD validation errors.
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.xml.sax.SAXParseException;
public class Validate {
public static void main(String[] args) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("pain.001.001.09.xsd"));
Validator validator = schema.newValidator();
try {
validator.validate(new StreamSource(new File(args[0])));
System.out.println("valid");
} catch (SAXParseException e) {
System.out.println("line " + e.getLineNumber() + ": " + e.getMessage());
}
}
}line 134: cvc-pattern-valid: Value 'FR00 NOT AN IBAN' is not facet-valid with respect to pattern '[A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30}' for type 'IBAN2007Identifier'.What the schema checks
A few of the constraints in the pain.001.001.09 schema:
| Element | Schema type | Constraint | BankLint rule |
|---|---|---|---|
MsgId, PmtInfId, InstrId, EndToEndId | Max35Text | 1 to 35 characters | BNK-ISO-004 |
Party names (Nm) | Max140Text | 1 to 140 characters | BNK-ISO-004 |
InstdAmt | ActiveOrHistoricCurrencyAndAmount | Decimal with at most 18 digits, of which at most 5 after the point, not negative; attribute Ccy required, three capital letters | BNK-ISO-007, BNK-ISO-008, BNK-ISO-009 |
IBAN | IBAN2007Identifier | Pattern [A-Z]{2,2}[0-9]{2,2}[a-zA-Z0-9]{1,30} | BNK-ISO-003 |
BICFI | BICFIDec2014Identifier | Pattern [A-Z0-9]{4,4}[A-Z]{2,2}[A-Z0-9]{2,2}([A-Z0-9]{3,3}){0,1} | BNK-ISO-003 |
ChrgBr | ChargeBearerType1Code | One of DEBT, CRED, SHAR, SLEV | BNK-ISO-005 |
AdrLine | Max70Text | Up to 7 occurrences of 1 to 70 characters |
The schema checks form, not meaning: the IBAN pattern accepts any two-letter country code and wrong check digits, and the currency pattern accepts any three capital letters.
Schema-valid is not SEPA-valid
The SEPA guidelines (EPC132-08 2025 v1.0) restrict many elements the schema allows. A file can pass xmllint and still break them:
| Element | ISO 20022 schema | SEPA | BankLint rule |
|---|---|---|---|
| Party names | Up to 140 characters | Up to 70 (items 1.7, 2.22, 2.68, 2.101, 2.117, 2.149) | SEPA-SCT-NAME-001, SEPA-SCT-NAME-002, SEPA-SCT-NAME-003 |
Address lines (AdrLine) | Up to 7 | Up to 2 (items 2.39 and 2.134) | SEPA-SCT-ADDR-001 |
Charge bearer (ChrgBr) | DEBT, CRED, SHAR or SLEV | SLEV only (items 2.75 and 2.98) | SEPA-SCT-CHRGBR-001 |
Currency (Ccy) | Any three capital letters | EUR only (item 2.95) | SEPA-SCT-CCY-001 |
| Amount decimals | Up to 5 | Up to 2 (item 2.95) | SEPA-SCT-AMT-002 |
The BankLint validator checks the schema and the SEPA rules together. Every SEPA rule is listed in SEPA Credit Transfer rules.
Questions
Where do I download the pain.001 XSD?
From the ISO 20022 site, iso20022.org: pain.001.001.09 and pain.001.001.03 are in its message archive under Payments Initiation. Use the version in the namespace of your file.
What does "No matching global declaration available for the validation root" mean?
The root element or its namespace does not belong to the schema, for example a pain.001.001.03 file checked against the pain.001.001.09 schema. Use the schema of the version in the file’s xmlns.
If the XSD validation passes, will the bank accept the file?
Not necessarily. The schema checks structure and formats only. SEPA rules and your bank’s own requirements come on top.
Sources: EPC132-08 2025 Version 1.0 (European Payments Council) and the ISO 20022 message schemas. All sources and dates: Standards and sources.
Validate a pain.001 fileFree, no account needed, files are not stored.