Kolos Hetyéssy Software developer

Free tools / SEPA & NACHA Payment File API

Validate a SEPA pain.001 or pain.008 file online

Drop a SEPA credit transfer (pain.001) or direct debit (pain.008) file below. You get the schema check, the SEPA rule check and a plain explanation of every problem, before the bank rejects the file.

  • Free, no sign-up
  • Your file is never stored
  • Deterministic: no AI guesswork

Try it now

The demo runs the same validation as the API. Your file is read in memory and discarded right after.

The live demo needs JavaScript, which seems to be switched off. The sample tabs show the engine's real output without it.

A pain.001.001.09 credit transfer generated by the API: structured addresses, one invoice reference and one RF creditor reference. Every check passes.

pain001-v09-valid.xml 1469 bytes · pain.001.001.09

Input file

<?xml version="1.0" ?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09">
  <CstmrCdtTrfInitn>
    <GrpHdr>
      <MsgId>PAYRUN-2026-10-23</MsgId>
      <CreDtTm>2026-10-21T09:00:00</CreDtTm>
      <NbOfTxs>2</NbOfTxs>
      <CtrlSum>1284.06</CtrlSum>
      <InitgPty>
        <Nm>Acme GmbH</Nm>
      </InitgPty>
    </GrpHdr>
    <PmtInf>
      <PmtInfId>PAYRUN-2026-10-23-1</PmtInfId>
      <PmtMtd>TRF</PmtMtd>
      <NbOfTxs>2</NbOfTxs>
      <CtrlSum>1284.06</CtrlSum>
      <PmtTpInf>
        <SvcLvl>
          <Cd>SEPA</Cd>
        </SvcLvl>
      </PmtTpInf>
      <ReqdExctnDt>
        <Dt>2026-10-23</Dt>
      </ReqdExctnDt>
      <Dbtr>
        <Nm>Acme GmbH</Nm>
        <PstlAdr>
          <TwnNm>Berlin</TwnNm>
          <Ctry>DE</Ctry>
        </PstlAdr>
      </Dbtr>
      <DbtrAcct>
        <Id>
          <IBAN>DE89370400440532013000</IBAN>
        </Id>
      </DbtrAcct>
      <DbtrAgt>
        <FinInstnId>
          <BICFI>COBADEFFXXX</BICFI>
        </FinInstnId>
      </DbtrAgt>
      <ChrgBr>SLEV</ChrgBr>
      <CdtTrfTxInf>
        <PmtId>
          <EndToEndId>INV-2026-17</EndToEndId>
        </PmtId>
        <Amt>
          <InstdAmt Ccy="EUR">1234.56</InstdAmt>
        </Amt>
        <Cdtr>
          <Nm>Supplier BV</Nm>
          <PstlAdr>
            <TwnNm>Amsterdam</TwnNm>
            <Ctry>NL</Ctry>
          </PstlAdr>
        </Cdtr>
        <CdtrAcct>
          <Id>
            <IBAN>NL91ABNA0417164300</IBAN>
          </Id>
        </CdtrAcct>
        <RmtInf>
          <Ustrd>Invoice 2026-17</Ustrd>
        </RmtInf>
      </CdtTrfTxInf>
      <CdtTrfTxInf>
        <PmtId>
          <EndToEndId>NOTPROVIDED</EndToEndId>
        </PmtId>
        <Amt>
          <InstdAmt Ccy="EUR">49.50</InstdAmt>
        </Amt>
        <Cdtr>
          <Nm>Office Supplies KG</Nm>
        </Cdtr>
        <CdtrAcct>
          <Id>
            <IBAN>DE02120300000000202051</IBAN>
          </Id>
        </CdtrAcct>
        <RmtInf>
          <Strd>
            <CdtrRefInf>
              <Tp>
                <CdOrPrtry>
                  <Cd>SCOR</Cd>
                </CdOrPrtry>
              </Tp>
              <Ref>RF18539007547034</Ref>
            </CdtrRefInf>
          </Strd>
        </RmtInf>
      </CdtTrfTxInf>
    </PmtInf>
  </CstmrCdtTrfInitn>
</Document>

Validation result

VALID 0 errors · 0 warnings

PAYRUN-2026-10-23
pain.001.001.09
Payment blocks
1
Transactions
2
Total
1284.06 EUR

No problems found. Schema, SEPA character set, totals, IBANs, addresses and references all pass.

JSON response POST /v1/validate
{
  "message_type": "pain.001.001.09",
  "kind": "credit_transfer",
  "profile": "epc",
  "valid": true,
  "error_count": 0,
  "warning_count": 0,
  "summary": {
    "message_id": "PAYRUN-2026-10-23",
    "payment_blocks": 1,
    "transactions": 2,
    "total_amount": "1284.06",
    "currency": "EUR"
  },
  "issues": []
}

A 2009 pain.001.001.03 file whose header control sum has two digits swapped. A German bank may reject the whole file for this.

pain001-v03-wrong-total.xml 1815 bytes · pain.001.001.03

Input file

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
  <CstmrCdtTrfInitn>
    <GrpHdr>
      <MsgId>OLD-2026-10-20-1</MsgId>
      <CreDtTm>2026-10-20T10:00:00</CreDtTm>
      <NbOfTxs>2</NbOfTxs>
      <CtrlSum>1248.06</CtrlSum>
      <InitgPty>
        <Nm>Acme GmbH</Nm>
        <Id><OrgId><BICOrBEI>COBADEFFXXX</BICOrBEI></OrgId></Id>
      </InitgPty>
    </GrpHdr>
    <PmtInf>
      <PmtInfId>OLD-2026-10-20-1-P1</PmtInfId>
      <PmtMtd>TRF</PmtMtd>
      <BtchBookg>true</BtchBookg>
      <NbOfTxs>2</NbOfTxs>
      <CtrlSum>1284.06</CtrlSum>
      <PmtTpInf><SvcLvl><Cd>SEPA</Cd></SvcLvl></PmtTpInf>
      <ReqdExctnDt>2026-10-23</ReqdExctnDt>
      <Dbtr>
        <Nm>Acme GmbH</Nm>
        <PstlAdr><Ctry>DE</Ctry><AdrLine>Hauptstr. 1</AdrLine><AdrLine>10115 Berlin</AdrLine></PstlAdr>
      </Dbtr>
      <DbtrAcct><Id><IBAN>DE89370400440532013000</IBAN></Id></DbtrAcct>
      <DbtrAgt><FinInstnId><BIC>COBADEFFXXX</BIC></FinInstnId></DbtrAgt>
      <ChrgBr>SLEV</ChrgBr>
      <CdtTrfTxInf>
        <PmtId><EndToEndId>INV-2026-17</EndToEndId></PmtId>
        <Amt><InstdAmt Ccy="EUR">1234.56</InstdAmt></Amt>
        <CdtrAgt><FinInstnId><BIC>INGBNL2A</BIC></FinInstnId></CdtrAgt>
        <Cdtr><Nm>Supplier BV</Nm></Cdtr>
        <CdtrAcct><Id><IBAN>NL91ABNA0417164300</IBAN></Id></CdtrAcct>
        <RmtInf><Ustrd>Invoice 2026-17</Ustrd></RmtInf>
      </CdtTrfTxInf>
      <CdtTrfTxInf>
        <PmtId><EndToEndId>NOTPROVIDED</EndToEndId></PmtId>
        <Amt><InstdAmt Ccy="EUR">49.50</InstdAmt></Amt>
        <Cdtr><Nm>Office Supplies KG</Nm></Cdtr>
        <CdtrAcct><Id><IBAN>DE02120300000000202051</IBAN></Id></CdtrAcct>
        <RmtInf><Ustrd>Order 5521</Ustrd></RmtInf>
      </CdtTrfTxInf>
    </PmtInf>
  </CstmrCdtTrfInitn>
</Document>

Validation result

INVALID 1 error · 1 warning

OLD-2026-10-20-1
pain.001.001.03
Payment blocks
1
Transactions
2
Total
1284.06 EUR

ERROR · CONTROL_SUM GrpHdr/CtrlSum The message control sum is 1248.06; the amounts add up to 1284.06.

WARNING · OLD_VERSION pain.001.001.03 is the 2009 version. Banks are moving to the 2019 versions (pain.001.001.09 / pain.008.001.08); German banks' specification limited the old version to November 2025. Use /v1/convert to migrate the file.

JSON response POST /v1/validate
{
  "message_type": "pain.001.001.03",
  "kind": "credit_transfer",
  "profile": "epc",
  "valid": false,
  "error_count": 1,
  "warning_count": 1,
  "summary": {
    "message_id": "OLD-2026-10-20-1",
    "payment_blocks": 1,
    "transactions": 2,
    "total_amount": "1284.06",
    "currency": "EUR"
  },
  "issues": [
    {
      "severity": "error",
      "rule": "CONTROL_SUM",
      "path": "GrpHdr/CtrlSum",
      "message": "The message control sum is 1248.06; the amounts add up to 1284.06."
    },
    {
      "severity": "warning",
      "rule": "OLD_VERSION",
      "path": "",
      "message": "pain.001.001.03 is the 2009 version. Banks are moving to the 2019 versions (pain.001.001.09 / pain.008.001.08); German banks' specification limited the old version to November 2025. Use /v1/convert to migrate the file."
    }
  ]
}

A pain.008.001.02 direct debit with a mistyped creditor identifier and a mandate dated in the future.

pain008-v02-errors.xml 1663 bytes · pain.008.001.02

Input file

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.008.001.02">
  <CstmrDrctDbtInitn>
    <GrpHdr>
      <MsgId>DD-2026-10-20-1</MsgId>
      <CreDtTm>2026-10-20T10:00:00</CreDtTm>
      <NbOfTxs>1</NbOfTxs>
      <CtrlSum>25.00</CtrlSum>
      <InitgPty><Nm>Sportverein Musterstadt e.V.</Nm></InitgPty>
    </GrpHdr>
    <PmtInf>
      <PmtInfId>DD-2026-10-20-1-RCUR</PmtInfId>
      <PmtMtd>DD</PmtMtd>
      <NbOfTxs>1</NbOfTxs>
      <CtrlSum>25.00</CtrlSum>
      <PmtTpInf>
        <SvcLvl><Cd>SEPA</Cd></SvcLvl>
        <LclInstrm><Cd>CORE</Cd></LclInstrm>
        <SeqTp>RCUR</SeqTp>
      </PmtTpInf>
      <ReqdColltnDt>2026-11-02</ReqdColltnDt>
      <Cdtr><Nm>Sportverein Musterstadt e.V.</Nm></Cdtr>
      <CdtrAcct><Id><IBAN>DE89370400440532013000</IBAN></Id></CdtrAcct>
      <CdtrAgt><FinInstnId><BIC>COBADEFFXXX</BIC></FinInstnId></CdtrAgt>
      <ChrgBr>SLEV</ChrgBr>
      <CdtrSchmeId>
        <Id><PrvtId><Othr><Id>DE98ZZZ09999999990</Id><SchmeNm><Prtry>SEPA</Prtry></SchmeNm></Othr></PrvtId></Id>
      </CdtrSchmeId>
      <DrctDbtTxInf>
        <PmtId><EndToEndId>MEMBER-0001-2026-11</EndToEndId></PmtId>
        <InstdAmt Ccy="EUR">25.00</InstdAmt>
        <DrctDbtTx><MndtRltdInf><MndtId>M-001</MndtId><DtOfSgntr>2026-12-01</DtOfSgntr></MndtRltdInf></DrctDbtTx>
        <DbtrAgt><FinInstnId><Othr><Id>NOTPROVIDED</Id></Othr></FinInstnId></DbtrAgt>
        <Dbtr><Nm>Anna Schmidt</Nm></Dbtr>
        <DbtrAcct><Id><IBAN>DE02120300000000202051</IBAN></Id></DbtrAcct>
        <RmtInf><Ustrd>Membership November</Ustrd></RmtInf>
      </DrctDbtTxInf>
    </PmtInf>
  </CstmrDrctDbtInitn>
</Document>

Validation result

INVALID 2 errors · 1 warning

DD-2026-10-20-1
pain.008.001.02
Payment blocks
1
Transactions
1
Total
25.00 EUR

ERROR · CREDITOR_ID PmtInf[1]/CdtrSchmeId/Id/PrvtId/Othr/Id 'DE98ZZZ09999999990' is not a valid SEPA creditor identifier.

ERROR · MANDATE_DATE PmtInf[1]/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/DtOfSgntr The mandate signature date is in the future.

WARNING · OLD_VERSION pain.008.001.02 is the 2009 version. Banks are moving to the 2019 versions (pain.001.001.09 / pain.008.001.08); German banks' specification limited the old version to November 2025. Use /v1/convert to migrate the file.

JSON response POST /v1/validate
{
  "message_type": "pain.008.001.02",
  "kind": "direct_debit",
  "profile": "epc",
  "valid": false,
  "error_count": 2,
  "warning_count": 1,
  "summary": {
    "message_id": "DD-2026-10-20-1",
    "payment_blocks": 1,
    "transactions": 1,
    "total_amount": "25.00",
    "currency": "EUR"
  },
  "issues": [
    {
      "severity": "error",
      "rule": "CREDITOR_ID",
      "path": "PmtInf[1]/CdtrSchmeId/Id/PrvtId/Othr/Id",
      "message": "'DE98ZZZ09999999990' is not a valid SEPA creditor identifier."
    },
    {
      "severity": "error",
      "rule": "MANDATE_DATE",
      "path": "PmtInf[1]/DrctDbtTxInf[1]/DrctDbtTx/MndtRltdInf/DtOfSgntr",
      "message": "The mandate signature date is in the future."
    },
    {
      "severity": "warning",
      "rule": "OLD_VERSION",
      "path": "",
      "message": "pain.008.001.02 is the 2009 version. Banks are moving to the 2019 versions (pain.001.001.09 / pain.008.001.08); German banks' specification limited the old version to November 2025. Use /v1/convert to migrate the file."
    }
  ]
}

How it works

  1. Export the payment file from your accounting, payroll or banking software. It is an XML file, usually .xml.
  2. Drop it into the box above. Tick the German option if the file goes to a German bank.
  3. Read the report: each problem has its rule, its location in the file and an explanation. The API returns the same report as JSON.

What is checked

Area Checks
File XML safety (no DTDs or entities), ISO 20022 XSD with line numbers
Text SEPA Latin character set (German profile: also ÄÖÜäöüß & *); references must not start or end with / or contain //; no empty elements; names ≤ 70
Totals NbOfTxs and CtrlSum on the group and every payment block, recomputed exactly
Amounts EUR only, 0.01–999,999,999.99, at most 2 decimals
Accounts IBAN check digits; SEPA creditor identifier check digits
Remittance one unstructured or one structured remittance (≤ 140 incl. tags), creditor reference type SCOR
Codes service level SEPA, charge bearer SLEV, payment method TRF / DD
Direct debits CORE or B2B (not mixed), sequence type, mandate signed not in the future
Addresses (2019) town and country when a structured or hybrid address is used; unstructured addresses are reported
Warnings dates in the past, duplicate end-to-end IDs, possible duplicate payments
German profile no byte order mark, UTF-8 only

Questions

Which versions are supported?

pain.001.001.03 and pain.001.001.09 (credit transfers), pain.008.001.02 and pain.008.001.08 (direct debits).

Which rules are checked?

The ISO 20022 schema (with line numbers) and the SEPA rules from the EPC customer-to-PSP implementation guidelines 2025. With the German option, also the German banks’ rules from DFÜ-Abkommen Anlage 3 v3.7.

What is the difference between an error and a warning?

Errors break the schema or a SEPA rule. Warnings point to things that are allowed but worth a look: dates in the past, duplicate end-to-end IDs, possible duplicate payments. Unstructured addresses in 2019 files are reported as well.

Does a valid report mean my bank will accept the file?

It means the file passes the ISO schema and the EPC rules (and the German rules if ticked). Bank-specific profiles other than German are not included yet, and a bank’s own duplicate detection may be stricter.

Is my file stored?

No. It is processed in memory and discarded right after the report is sent back. See the privacy notice.

Need it in your own software?

The same engine is available as an API: whole files instead of a preview, every export format, automation.

SEPA & NACHA Payment File API →

Related tools

Free toolSEPA & NACHA Payment File API

pain.001.001.03 to .09

Migrate 2009 SEPA files to the 2019 versions, checked against the new schema and the SEPA rules.

Free toolSEPA & NACHA Payment File API

NACHA file validator

Check a NACHA ACH file record by record, with each problem's line, column and a plain explanation.

Free toolSEPA & NACHA Payment File API

CSV to NACHA

Turn a payroll or vendor CSV into a NACHA file that passes every check before it goes to the bank.