Bank profiles
A bank profile is an overlay on the always-correct SEPA core, never a fork of it. Profiles let you express what a specific bank requires beyond the standard SEPA XSD: extra validation rules and optional minor output tweaks.
What a profile is
Section titled “What a profile is”A BankProfile has two parts:
- Check functions (
checkCreditTransfer,checkDirectDebit): returnProfileIssue[]. An empty array means the document passes. - Output options (
output): minor additive tweaks such asbatchBooking. These options only set elements the XSD already permits. A profile can never make the output XSD-invalid.
A profile is additive. It runs after the base Zod validation, as a second pass. It never replaces or weakens any base rule.
A profile is not a different message schema. If you need a different XML namespace (e.g.
German DK pain.001.003.03), use the variant option, not a profile. See
National variants.
The requireBic profile
Section titled “The requireBic profile”Some banks reject IBAN-only files even though the SEPA rulebook and the XSD make BIC optional
since 2016. The built-in requireBic profile catches this at validation time.
import { writeCreditTransfer, validateCreditTransfer, requireBic,} from 'sepa-xml-ts'
// Validate with the profileconst result = validateCreditTransfer(doc, { profile: requireBic })if (!result.ok) { console.error(result.errors) // base Zod issues console.error(result.profileIssues) // requireBic issues (missing BIC)}
// Write: throws if base validation or profile check failsconst xml = writeCreditTransfer(doc, { profile: requireBic })Same API for direct debit:
import { writeDirectDebit, validateDirectDebit, requireBic } from 'sepa-xml-ts'
const result = validateDirectDebit(doc, { profile: requireBic })const xml = writeDirectDebit(doc, { profile: requireBic })Output options: batchBooking
Section titled “Output options: batchBooking”Profiles can request minor output tweaks via output. The batchBooking option emits
<BtchBookg>true</BtchBookg> (or false) in each PmtInf element, at the XSD-correct position
after PmtMtd and before NbOfTxs. The output is XSD-valid. The parser ignores the element,
so the round-trip is unaffected.
import { writeCreditTransfer, type BankProfile } from 'sepa-xml-ts'
const myBankProfile: BankProfile = { id: 'my-bank', output: { batchBooking: true },}
const xml = writeCreditTransfer(doc, { profile: myBankProfile })// Each PmtInf now contains: <BtchBookg>true</BtchBookg>Authoring your own profile
Section titled “Authoring your own profile”Implement the BankProfile interface. Check functions receive the validated model and return
ProfileIssue[]. Use dot-delimited path values to point at the offending field.
import type { BankProfile, ProfileIssue } from 'sepa-xml-ts'
export const myProfile: BankProfile = { id: 'my-bank-rules', description: 'Extra rules required by My Bank AG',
checkCreditTransfer(doc): ProfileIssue[] { const issues: ProfileIssue[] = [] for (const [bi, batch] of doc.batches.entries()) { if (batch.transfers.length > 100) { issues.push({ path: `batches.${bi}.transfers`, message: 'My Bank AG rejects batches with more than 100 transfers', }) } for (const [ti, transfer] of batch.transfers.entries()) { if (!transfer.creditor.bic) { issues.push({ path: `batches.${bi}.transfers.${ti}.creditor.bic`, message: 'My Bank AG requires BIC on all creditor accounts', }) } } } return issues },
checkDirectDebit(doc): ProfileIssue[] { // Return [] if no additional rules apply to direct debits return [] },}Combining profile and variant
Section titled “Combining profile and variant”The profile and variant options are independent and can be combined:
const xml = writeCreditTransfer(doc, { variant: 'pain.001.003.03', profile: requireBic,})The profile runs against the validated model. The variant controls the XML serialization. Both can be active at the same time.