POST
Step 1. Send the items to 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 its batchId. 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 building payments yourself, you can post a CSV or an ISO 20022 pain.001 file to this route and let us parse it:
Send either 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.
camt.053 is not a payment file. It reports what has already happened on an account and carries no instruction to pay anyone; sending one is rejected. You want pain.001.
If the parser could not use something in the file — an unrecognised 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.
This route serves only verification. The 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.
Availability. This route is rolled out per program and returns 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

Authorization
string
header
required

The caller's id_token from authentication — the ID token, not the access_token. The program and environment come from the token.

Path Parameters

customerId
string
required

The customer id.

Body

application/json

Send either payments or file — exactly one. A request carrying both is refused rather than resolved by precedence.

payments
object[]
required

The payment items to validate. Required unless a file is sent instead.

action
enum<string>

Optional — this route serves only verify, so a missing value defaults to it. Any other value is rejected with 403.

Available options:
verify
file
object

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.

batchId
string<uuid>

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.

requestId
string<uuid>

Correlates this verification; poll GET /v1/batch-payments/verify/{requestId} with it.

status
enum<string>

Always PENDING — validation runs in the background.

Available options:
PENDING
batchId
string<uuid>

The draft created (or updated). Submit with this id to pay it.

expiresAt
string<date-time>

After this instant the draft can no longer be submitted.

warnings
string[]

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.