curl --request POST \
--url https://api.next.orenda.finance/v1/customers/{customerId}/batch-payments/verify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"action": "verify",
"payments": [
{
"payer": {
"account": {
"type": "uk",
"sortCode": "010203",
"accountNumber": "12345678"
}
},
"payee": {
"name": "Ada Lovelace",
"account": {
"type": "uk",
"sortCode": "040506",
"accountNumber": "87654321"
},
"accountType": "CONSUMER"
},
"reference": "Invoice 1024",
"amount": "150.00"
},
{
"payer": {
"account": {
"type": "iban",
"iban": "DE89370400440532013000"
}
},
"payee": {
"name": "Grace Hopper",
"account": {
"type": "iban",
"iban": "FR7630006000011234567890189",
"bic": "AGRIFRPP"
}
},
"reference": "Invoice 1025",
"amount": "200.00"
}
]
}
'{
"success": true,
"requestId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "PENDING",
"batchId": "3f1c8f9e-1d2b-4a3c-9e5f-6a7b8c9d0e1f",
"expiresAt": "2026-08-17T09:00:00.000Z"
}{
"success": false,
"code": "VALIDATION_ERROR",
"message": "payee.sortCode must be 6 digits"
}{
"success": false,
"code": "UNAUTHORIZED",
"message": "Unauthorized"
}{
"success": false,
"code": "FORBIDDEN",
"message": "Forbidden"
}{
"success": false,
"code": "FUNDING_FAILED",
"message": "Insufficient available balance"
}{
"success": false,
"code": "SCA_REQUIRED",
"message": "Strong Customer Authentication required"
}{
"success": false,
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal Server Error"
}Verify a batch
Submit the items for validation and a payee name check.
curl --request POST \
--url https://api.next.orenda.finance/v1/customers/{customerId}/batch-payments/verify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"action": "verify",
"payments": [
{
"payer": {
"account": {
"type": "uk",
"sortCode": "010203",
"accountNumber": "12345678"
}
},
"payee": {
"name": "Ada Lovelace",
"account": {
"type": "uk",
"sortCode": "040506",
"accountNumber": "87654321"
},
"accountType": "CONSUMER"
},
"reference": "Invoice 1024",
"amount": "150.00"
},
{
"payer": {
"account": {
"type": "iban",
"iban": "DE89370400440532013000"
}
},
"payee": {
"name": "Grace Hopper",
"account": {
"type": "iban",
"iban": "FR7630006000011234567890189",
"bic": "AGRIFRPP"
}
},
"reference": "Invoice 1025",
"amount": "200.00"
}
]
}
'{
"success": true,
"requestId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "PENDING",
"batchId": "3f1c8f9e-1d2b-4a3c-9e5f-6a7b8c9d0e1f",
"expiresAt": "2026-08-17T09:00:00.000Z"
}{
"success": false,
"code": "VALIDATION_ERROR",
"message": "payee.sortCode must be 6 digits"
}{
"success": false,
"code": "UNAUTHORIZED",
"message": "Unauthorized"
}{
"success": false,
"code": "FORBIDDEN",
"message": "Forbidden"
}{
"success": false,
"code": "FUNDING_FAILED",
"message": "Insufficient available balance"
}{
"success": false,
"code": "SCA_REQUIRED",
"message": "Strong Customer Authentication required"
}{
"success": false,
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal Server Error"
}POST /v1/customers/{customerId}/batch-payments/verify. We
validate each item against your program’s rules and run a name check on the payee
(Confirmation of Payee for UK programs, Verification of Payee for EU programs).
The response returns a requestId, a batchId, and expiresAt. Validation runs in the
background, so next check the status with that requestId.
The draft
Verifying creates a draft batch and returns itsbatchId. The draft records that these
items are prepared and awaiting submission — it is what lets one person prepare a batch and
another pay it, by sending that batchId to submit.
Re-verifying after edits? Send the same batchId back here. The draft is updated in place
and keeps its original creation time, so you get one draft that changes rather than a new one
per edit.
A draft expires at expiresAt — 7 days, or the end of its earliest scheduledDate,
whichever comes first. After that it can no longer be submitted. The scheduled-date bound
matters: a batch whose scheduled date has passed is rejected at submit as a whole, not
row-by-row, so the draft is already unusable by then.
Remember to send the right destination fields per item: UK uses accountNumber + sortCode,
EU uses iban + bic.
Sending a file instead
Rather than buildingpayments yourself, you can post a CSV or an ISO 20022 pain.001 file to
this route and let us parse it:
{
"action": "verify",
"file": { "content": "Payee Name,Payee IBAN,…", "format": "csv" }
}
payments or file, not both. format is optional — omitted, it is detected
from the content. The response and everything after it are unchanged: the file becomes exactly
the items you would have sent by hand, and each row is validated the same way, so a bad row
comes back in invalidPayments rather than being dropped.
Only verify takes a file, and only on this route — the older single endpoint refuses one.
Submit takes the validPayments you get back from
checking the status.
pain.001 is the ISO 20022 credit transfer initiation message, any version from .03 to
.09. Namespace prefixes, BIC and BICFI, and all three ReqdExctnDt forms — bare, Dt
and DtTm — are handled. UK sort codes are read from
CdtrAgt/FinInstnId/ClrSysMmbId/MmbId — but only when ClrSysId/Cd says GBDSC, or no
scheme is named and the payee has no IBAN, so a German DEBLZ Bankleitzahl is not mistaken
for one — and the reference from RmtInf/Ustrd — joined if it
repeats — falling back to RmtInf/Strd/CdtrRefInf/Ref.
CSV is the published batch template. The header row is found by looking for the
Payee Name column, so a title row above it is fine, and quoted fields are honoured so a payee
name containing a comma does not shift the rest of the row. Amount must be dot-decimal
(1234.56) — a decimal comma in an unquoted cell splits it in two and shifts every later
column. Any row whose column count differs from the header raises a warning naming the row;
read those before submitting.
Payee AccountType, an
EndToEndId outside the 2–35 character range the rails accept — the response carries a
warnings array saying so. The payments are still queued; the field is absent when there is
nothing to report.
Two behaviours to know before sending a file that has been sitting around.
A past ReqdExctnDt is kept, not corrected — we will not quietly move it to today and pay
it. Where you see the consequence is the draft: expiresAt is the earliest scheduled date or
seven days, whichever comes first, so a past date means the 200 from verify already carries
an elapsed expiresAt and the draft is unusable from the moment it exists.
Submit then fails on draft expiry, with an error that does not
mention the date.
And InstdAmt/@Ccy is not applied, since payments settle in the payer account’s currency.
Dates must be YYYY-MM-DD or YYYY/MM/DD (single-digit month or day is padded for you).
Anything else is passed through and comes back as an invalid row rather than being guessed
at — 01/09 is a different day in different countries.
action field is optional here — omit it and it
defaults to "verify". Sending "initiate", "passkey-challenge" or "submit" returns
403; those belong to submit.Nothing is charged or reserved by this call. You can verify the same items as many times as
you like while the user edits them.403 until yours is
enabled. Until then, send action: "verify" to
POST /v1/customers/{customerId}/batch-payments — the request and response bodies are
identical, except that the older route does not accept a file. Sending items as
payments works on both. See
the overview.Multi-customer and back office
Acting on behalf of many customers in one batch? Use the path-less form,POST /v1/batch-payments/verify, which resolves each item’s payer account against you.
It is back-office only and rejects any account you are not responsible for.Authorizations
The caller's id_token from authentication — the ID token, not the access_token. The program and environment come from the token.
Path Parameters
The customer id.
Body
- Option 1
- Option 2
Send either payments or file — exactly one. A request carrying both is refused rather than resolved by precedence.
The payment items to validate. Required unless a file is sent instead.
Show child attributes
Show child attributes
Optional — this route serves only verify, so a missing value defaults to it. Any other value is rejected with 403.
verify A CSV or pain.001 file to parse into payments instead of sending payments. Exactly one of payments / file is required; sending both is rejected.
Show child attributes
Show child attributes
Optional. The draft being re-verified after edits — the draft is updated in place and keeps its original creation time. Omit to create a new draft.
Response
Accepted for validation. Returns requestId, the draft's batchId and its expiresAt; poll GET /v1/batch-payments/verify/{requestId} for the outcome.
Correlates this verification; poll GET /v1/batch-payments/verify/{requestId} with it.
Always PENDING — validation runs in the background.
PENDING The draft created (or updated). Submit with this id to pay it.
After this instant the draft can no longer be submitted.
Present only when the uploaded file contained something the parser could not carry across — an unrecognised column value, an EndToEndId outside the length or charset the rails accept, a file mixing currencies. Not row rejections: the payments are still queued for verification. Absent when there is nothing to report, and never present for a payments request.