Replace file-based Bulk/Batch Payments with a JSON array
Deprecate the unused file-upload model for Bulk/Batch Payments and carry the individual payments as a JSON array in a single signed request.
Cast your vote
Sign in with the Trust Framework to vote — For, Against, or Abstain — recorded in the open with your reasoning. Your organisation and name come from your directory profile, and each person may vote once.
A file model nobody has built
File-based Bulk/Batch Payments — referred to in the standard as File Payments — have been part of the specification since v1.2. They are defined as a file upload model: a TPP uploads a payment file in an LFI-specific format to POST /payment-consents/{ConsentId}/file, declares its FileType, FileHash, NumberOfTransactions and ControlSum on the consent, then creates the batch with POST /file-payments. The execution report is returned as a file as well.
In practice, no LFI in the ecosystem has implemented these endpoints. There is not a single live File Payment integration — which means there is nothing to migrate, and the model can be revised at effectively zero cost before banks begin building file-parsing and malware-scanning pipelines to support it.
Beyond being unbuilt, the file model is under-specified: an LFI could not implement it today without a round of clarifications. A few examples — not an exhaustive list — are below:
- The file holds PII, but the specification defines no encryption element to protect it.
- The specification does not cover how uploaded files are scanned.
- It does not define the required file structures, which file types must be supported, or how a TPP discovers which file types each LFI accepts.
Carry the payments as a JSON array
Deprecate the file-upload mechanism and carry the payments inline instead. The Bulk/Batch create request — POST /file-payments today — would carry the individual payments as a JSON array in a single signed request, one array element per transaction, in place of a reference to an uploaded file. (Whether the endpoint and schemas keep the file-payments name is an open question for the ecosystem — see the asks below.)
Each element of Instructions[] is assembled from the single-payment fields the standard already defines (amount, creditor account, references). The request is signed end-to-end exactly like POST /payments, so the same validation, signing, and idempotency rules apply to a batch as to a single payment. The NumberOfTransactions and ControlSum integrity checks are retained — now computed over the array rather than a file.
A TPP can still collect a file from the customer — for example, an Excel sheet of ten payments. The difference is where it is converted: the TPP maps that file into the JSON array per its own specification and passes the result to the LFI, who processes the payments from the array. There is little customer impact — the same file is still the customer’s starting point. What moves is the conversion: the TPP turns the file into JSON-format payments rather than the LFI parsing the file itself.
What changes in the spec
Three concrete changes — to the consent, the authorization experience, and the payment-creation request.
Remove FileType, FileHash, and FileReference — there is no file to type, hash, or reference. Add Description: the reason the LFI shows the customer at authorization (for example, “Payroll June 2026”). NumberOfTransactions, ControlSum, and RequestedExecutionDate stay — the LFI validates the JSON array’s count and total against NumberOfTransactions and ControlSum.
The authorization page no longer renders an uploaded file. It shows the standard confirm-payment details, with Description as the stated reason. The change for the customer is minimal — the consent confirmation looks like any other payment.
POST /file-payments bodyThe create request carries everything POST /payments carries, with two structural changes: the payments become an Instructions[] array — one element per transaction — and PersonalIdentifiableInformation, DebtorReference, and CreditorReference move inside each element, so every instruction is self-contained. CurrencyRequest is dropped to remove complexity. ConsentId, PaymentPurposeCode, and OpenFinanceBilling stay at the batch level. The request is a signed JWS end-to-end, exactly like POST /payments.
# 1 - Upload the payment file: any format the LFI happens to accept
POST /payment-consents/{ConsentId}/file
Content-Type: */* # CSV, ISO 20022 XML, fixed-width...
# 2 - Reference the file by hash on the consent instruction
"Instruction": {
"FileType": "UK.OBIE.pain.001.001.08",
"FileHash": "OErCwePj...", # base64 SHA-256 of the bytes above
"FileReference": "payroll-2026-06",
"NumberOfTransactions": 250,
"ControlSum": "125000.00"
}POST /file-payments
Content-Type: application/jwt # signed JWS, exactly like POST /payments
{
"Data": {
"ConsentId": "pcon_8821",
"PaymentPurposeCode": "SALA",
"OpenFinanceBilling": { ... }, # unchanged from POST /payments
"Instructions": [
{
"Amount": { "Amount": "1000.00", "Currency": "AED" },
"PersonalIdentifiableInformation": "eyJhbGciOiJSU0Et...", # JWE: creditor account + PII
"DebtorReference": "payroll-jun26",
"CreditorReference": "emp-001"
},
{
"Amount": { "Amount": "750.00", "Currency": "AED" },
"PersonalIdentifiableInformation": "eyJhbGciOiJSU0Et...",
"DebtorReference": "payroll-jun26",
"CreditorReference": "emp-002"
}
]
}
}
# API Hub validates the array against the consent:
# Instructions.length == NumberOfTransactions
# sum(Amount) == ControlSumThe array model, written out
Two working drafts attached to this proposal — the revised PAR consent object and the JSON-array create request — each opens in the same rendered view as the published API specs. Schemas the proposal changes are defined in full; schemas unchanged from the published spec are shown as stubs marked “unchanged”.
What moving to JSON removes
- ✓No CSV parsing, character-encoding, or line-ending handling.
- ✓No partial or corrupted uploads — and no single malformed record that fails an entire file.
- ✓No malware scanning of uploaded bytes.
- ✓No additional encryption layer to protect the PII held at rest in a file.
- ✓No "wrong file format" error class to specify, return, and handle.
- ✓No ecosystem-wide negotiation of which file formats each LFI accepts.
- ✓The API Hub can validate the array’s count and total against NumberOfTransactions and ControlSum centrally — each LFI no longer has to build that check itself.
- ✓Reuses the established single-payment shape — nothing new for implementers to learn.
- ✓Adjacent areas (error-code mapping, the risk block) change incrementally, not structurally.
What moving to JSON costs
- ×Existing back-office files (ISO 20022 pain.001, CSV) the file model accepted as-is must now be mapped to JSON at the TPP boundary.
- ×A file upload can stream; a single JSON request is held in memory and bounded by request-size limits.
