# SepaCheck (checksepa.com) > Free online validator for SEPA credit transfer files (ISO 20022 pain.001.001.03 and pain.001.001.09). Checks XML structure and a basic EPC SEPA Credit Transfer baseline and returns exact error locations. Payment files are processed in memory and not stored. Operated by Bobook Limited (Dublin, Ireland). ## What it checks - ISO 20022 pain.001 structure: required elements, types, choices, version-specific fields (e.g. ReqdExctnDt) - IBAN mod-97 and official length per country (debtor and creditor) - CtrlSum and NbOfTxs at group and payment-info level - Charge bearer must be SLEV for SEPA (DEBT/CRED/SHAR rejected) ## Supported - pain.001.001.03: yes - pain.001.001.09: yes - pain.001.001.11: not yet - pain.008 (SEPA Direct Debit): not yet (recognised, returned as valid: false) - camt: not yet (recognised, returned as valid: false) - Bank-specific Implementation Guidelines (MIG overlays, e.g. DK-TVS/GBIC): out of scope on the free check. A file that passes can still be rejected by a bank. - Max file size: 1.5 MB on the website, 2 MB on POST /v1/validate ## Privacy - Payment files are validated in memory and discarded. The XML is not written to disk. - Server logs keep message version, byte size, pass/fail, error codes, and a hash of the client IP. The XML is not logged. - XML is never rewritten by or sent to a language model. - No bank login, no EBICS, no Verification of Payee, no payment initiation. ## API - POST /v1/validate is live. No API key. - Send multipart form field "file", or a raw XML body with Content-Type: application/xml. - Free: 30 validations per IP per hour, and 200 per IP per UTC day. Above that: HTTP 429, Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-RateLimit-Limit-Day, X-RateLimit-Remaining-Day, and a link to the waitlist. - Max file size: 2 MB. Larger files: HTTP 413. Empty body: HTTP 400. - JSON report: valid, format (pain.001), version (pain.001.001.03 or pain.001.001.09), errors[{code,message,path,line}], warnings (same item shape). - GET /v1/validate returns 405. The website route POST /api/validate is a different response and is not this contract. - Higher limits and API keys: waitlist, from EUR 79/month; joining is free, charged only after confirmation - Join: https://checksepa.com/api-waitlist (or email info@bobook.club, subject "checksepa API waitlist") - Docs: https://checksepa.com/docs - OpenAPI: https://checksepa.com/openapi.json ## Key pages - [Validate a pain.001 file](https://checksepa.com/validate): upload and see errors - [CSV to pain.001 generator](https://checksepa.com/generate): builds a basic pain.001.001.03 from CSV (columns: debtor_name, debtor_iban, creditor_name, creditor_iban, amount) - [API docs](https://checksepa.com/docs) - [Sample files](https://checksepa.com/samples): valid and invalid pain.001 examples with dummy IBANs - [Changelog](https://checksepa.com/changelog) - [Privacy](https://checksepa.com/privacy) ## Contact Bobook Limited, Venture Hub, 136 Capel Street, Dublin 1, D01 T2C9, Ireland ยท info@bobook.club # API docs (plain text of /docs) Status: single-file API is live and free. Higher limits and API keys stay on the waitlist. POST /v1/validate checks one SEPA XML file, with no API key. Free up to 30 validations per IP per hour and 200 per IP per UTC day. Above that: HTTP 429 with Retry-After. Files over 2 MB: HTTP 413. An empty body: HTTP 400. GET /v1/validate: HTTP 405. Higher limits and API keys: from EUR 79/month. Joining the waitlist is free; charged only after confirmation. Join: https://checksepa.com/api-waitlist or mailto:info@bobook.club?subject=checksepa%20API%20waitlist What it checks: ISO 20022 pain.001.001.03 and pain.001.001.09. Required structure, version-specific ReqdExctnDt, IBAN mod-97 and country length, CtrlSum and NbOfTxs at group and payment-info level, charge bearer SLEV (DEBT, CRED, and SHAR are errors). pain.008, camt.053, camt.054, and other ISO 20022 messages are detected (format and version) and returned with valid: false. They are not rule-checked yet. pain.001.001.11 is not supported yet. Result fields: valid (boolean), format (for example pain.001), version (for example pain.001.001.09), errors[], warnings[]. Each finding has code, message, path, and line. path is an XPath-like location such as /Document/CstmrCdtTrfInitn/GrpHdr/CtrlSum. line is 1-based, or null when it cannot be placed. Codes look like EPC-CTRLSUM, EPC-IBAN, EPC-NBOFTXS, EPC-CHRGBR, XSD-REQUIRED, XSD-VERSION. The website route POST /api/validate also returns ok, issues (with severity, source, location, hint), resultClass, sessionId, and uploadIndexInSession. Those fields are not in POST /v1/validate. Request, multipart: curl -F "file=@pain001.xml" https://checksepa.com/v1/validate Request, raw XML: curl -X POST https://checksepa.com/v1/validate -H "Content-Type: application/xml" --data-binary @pain001.xml Example response from the published invalid pain.001.001.03 sample (first error only; the full report has more, including EPC-IBAN, EPC-NBOFTXS, EPC-CHRGBR, and XSD-REQUIRED): { "valid": false, "format": "pain.001", "version": "pain.001.001.03", "errors": [ { "code": "EPC-CTRLSUM", "message": "CtrlSum is 999.00 but the transactions sum to 175.50.", "path": "/Document/CstmrCdtTrfInitn/GrpHdr/CtrlSum", "line": 8 } ], "warnings": [] } A valid pain.001.001.03 or pain.001.001.09 sample returns valid: true, errors: [], warnings: []. Rate-limit headers on a counted response: - X-RateLimit-Limit: hourly limit (30) - X-RateLimit-Remaining: remaining in the current UTC hour - X-RateLimit-Reset: Unix time when the current UTC hour ends - X-RateLimit-Limit-Day: daily limit (200) - X-RateLimit-Remaining-Day: remaining in the current UTC day 429 also sets Retry-After (seconds). The JSON error is rate_limit. The JSON limit field is hourly or daily. 429 body includes waitlist https://checksepa.com/api-waitlist and docs https://checksepa.com/docs. Privacy: the file is processed only in the memory of that HTTP request. It is not written to disk, not logged as XML, and not sent to a language model. Technical events contain no file contents, only metadata (byte size, format, version, error count, a hash of the client IP). Rate-limit counters are in process memory and are not a copy of the file. Scope: a file can pass here and still be rejected by a bank. Bank MIG overlays are out of scope. No bank login, no EBICS, no Verification of Payee, no payment initiation, no legal advice. Samples (dummy IBANs): https://checksepa.com/samples OpenAPI: https://checksepa.com/openapi.json # FAQ (homepage) Which SEPA XML versions does SepaCheck validate? pain.001.001.03 and pain.001.001.09 (SEPA credit transfer). pain.008 and pain.001.001.11 are not supported yet. camt is not supported yet. The API still returns format and version for a pain.008 or camt file, with valid: false. Are my payment files stored? No. The website and POST /v1/validate both process the file in memory and discard it. Logs keep version, size, pass/fail and error codes. The XML is not logged. If my file passes, will my bank accept it? Not necessarily. SepaCheck checks ISO 20022 structure and a basic EPC baseline. Banks add their own implementation guidelines, which are out of scope on the free check. Is there an API? Yes. POST https://checksepa.com/v1/validate is free and needs no API key. The limit is 30 validations per IP per hour and 200 per UTC day, with a 2 MB maximum. Higher limits and API keys are on the waitlist, planned from EUR 79/month, and charged only after confirmation.