Quickstart
This guide walks through the full round-trip: build a model, validate it, write it to pain.001 XML, and parse it back into the original model.
-
Install.
Terminal window npm install sepa-xml-ts -
Build a CreditTransferDocument.
import { euros, type CreditTransferDocument } from 'sepa-xml-ts'const doc: CreditTransferDocument = {messageId: 'MSG-2026-0001',createdAt: '2026-06-01T10:30:00Z', // ISO 8601 datetimeinitiatingParty: 'ACME GmbH',batches: [{id: 'BATCH-001',executionDate: '2026-06-03', // YYYY-MM-DD, never a datetimedebtor: {name: 'ACME GmbH',iban: 'DE89370400440532013000',bic: 'COBADEFFXXX', // optional since 2016 SEPA rulebook},transfers: [{endToEndId: 'INV-1001',amount: euros('123.45'),creditor: {name: 'Beispiel AG',iban: 'NL91ABNA0417164300',},remittanceInfo: 'Invoice 1001', // optional, max 140 chars},],},],}euros('123.45')returns{ currencyCode: 'EUR', minorUnits: 12345n }. Passing a float or a string with more than two decimal places throws immediately. -
Validate the model.
import { validate } from 'sepa-xml-ts'const result = validate(doc)if (!result.ok) {// result.errors is an array of Zod issuesconsole.error(result.errors)process.exit(1)}// result.data is the validated CreditTransferDocumentvalidateruns the full Zod schema including all business-rule refinements (IBAN checksum, SEPA charset, money constraints). It returns a typed result instead of throwing. -
Write to XML.
import { writeCreditTransfer } from 'sepa-xml-ts'const xml = writeCreditTransfer(result.data)// xml starts with:// <?xml version="1.0" encoding="UTF-8"?>// <Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09" ...>The writer derives
NbOfTxs,CtrlSum,PmtMtd=TRF,SvcLvl/Cd=SEPA, andChrgBr=SLEVfrom the model. You cannot emit a structurally invalid file. -
Parse back.
import { parse } from 'sepa-xml-ts'const parsed = parse(xml)if (!parsed.ok) {console.error(parsed.error)} else if (parsed.type === 'pain.001') {// parsed.data is a CreditTransferDocumentconsole.log(parsed.data.messageId) // 'MSG-2026-0001'}parseauto-detects the message type and returns a discriminated union. The round-trip property holds:parse(write(model))deep-equals the original model. -
Optional: validate against the official EPC XSD.
import { validateXsd } from 'sepa-xml-ts/xsd'const xsdResult = await validateXsd(xml)console.log(xsdResult.valid) // trueThis lazy-loads
libxml2-wasm. It is an optional belt-and-suspenders check. The library already enforces every constraint that the XSD covers via the Zod schema and the writer.
Direct debit quickstart
Section titled “Direct debit quickstart”For a direct-debit equivalent, replace CreditTransferDocument with DirectDebitDocument and
writeCreditTransfer with writeDirectDebit. See Direct debits for the
full model and writer API.