Batch Payments
This describes Acme's Batch Payments API. This API can be used to send multiple payments in a single batch to the bank using file transfer.
Which API should I use?
Payments created outside of a Batch (using the Payments API) are sent to the bank over API transport. Payments created within a batch using the Batch Payments API described here are sent to the bank over files (host-to-host integration).
The API you should use to create the payment depends on the integration you have with your bank.
Both kinds of payments are visible in the List Payments API.
Payment Batch API
The Payment Batch API creates a batch of payments all at once. The payments in
a batch share a common payment type, payment date, and source account. They
also share a common currency by default. Each payment may set its own
currency to override the batch-level currency. The payments in a batch will
be submitted together in a single file to the bank. Each payment in the batch
has its own individual ID that can be used to track its success / failure. The
batch itself also has an ID.
Upon receiving the batch, Acme will process and send it to the bank. Any progress reports will be sent via webhook. The changes will also be visible when calling the list or get APIs. A sample timeline of events and webhooks / status changes is provided below.
Create a Batch Payment
{
"type": "MEPS",
// actual payment date may change due to auto value date rollover from bank
// e.g. due to submitting batch after cutoff, or approval taking too long
"paymentDate": "2024-05-10",
"senderAccountId": "intacct_XYZ",
// optional - must be provided if account has multiple currencies configured in Acme
"senderAccountCurrency": "SGD",
"currency": "SGD",
"payments": [
{
"amount": 20000,
// optional - defaults to the batch-level currency if not provided
// set this to override the batch-level currency for this payment
"currency": "SGD",
// optional - a random string will be generated if not provided
// cannot reuse previous customer references (checked by bank)
"customerReference": "DONUTS",
// optional
"paymentDetails": "10 boxes of 12 each",
// optional - for TT / MEPS: SENDER/RECEIVER/SHARED
"bankChargeBearer": "SENDER",
"receiver": {
"name": "Donut Shop",
"bank": "OCBCSGSGXXX",
"bankAccountNumber": "123456789",
// address must be provided for TT/MEPS
"address": {
"line1": "20 Side Street",
"line2": "Unit 02-3A"
}
}
},
{
"amount": 3000
"customerReference": "COFFEE",
"receiver": {
"name": "Coffee Shop",
"bank": "UOVBSGSGXXX",
"bankAccountNumber": "345678901",
"address": {
"line1": "127 Jln Merdeka"
}
}
},
[...]
]
}More details on the receiver fields may be found in our Payment documentation.
Response
200 OK
{
"id": "pymtb_XYZ",
"type": "MEPS",
"paymentDate": "2024-05-10",
"senderAccountId": "intacct_XYZ",
"currency": "SGD"
"senderAccountCurrency": "SGD",
"status": "PROCESSING", // PROCESSING (still at Acme) -> SUBMITTED (at the bank) -> FAILED / COMPLETED
"payments": [
{
"id": "pymt_XYZ",
"amount": 20000,
"receiver": { ... },
"status": "PROCESSING" // individual payment status
},
{
"id": "pymt_XYZ",
"amount": 3000,
"receiver": { ... },
"status": "PROCESSING"
},
[...]
]
}The individual payment objects (pymt_XXX) returned as part of the response are also
visible in our Payments API endpoints.
Validation
- Sender account currency: must be specified if account is configured in Acme as multi-currency
- Currency
- MEPS/FAST/PAYNOW: must be SGD
- Number of payments in one batch: maximum of 1000
- Receiver name: must be present
- Receiver address
- TT/MEPS: must be present
- ACT: must not be present
- all except ACT: if present, should fit within 3x35 character lines
- Receiver bank (for bank account payments - ACT/TT/MEPS/PAYNOW):
- ACT: must not be present
- others: must be present
Possible Errors
For the 4xx and 503 answers below, Acme created nothing, so nothing reached the bank. Only 400 and 429 carry an errorCode.
| Status | Body | What it means | What to do |
|---|---|---|---|
400 | errorCode INVALID_REQUEST_PARAMETER, errorMessage, and paymentErrors when payment fields failed | The request failed validation. errorMessage names the first field and the rule it broke. paymentErrors lists each failing payment field, with the payment's position in payments (from 0). Acme rejects the whole batch if any payment fails. | Fix every listed field and send the whole batch again, with a new Idempotency-Key (see Idempotency). |
401 | Empty | The API key is missing, wrong or disabled, or the request came from an IP address your organization does not allow. | Check the API key (a test key for test mode, a live key for live mode). If it is right, contact Acme. |
403 | No errorCode | The API key does not have the Batch Payments permission. | Contact Acme. |
409 | Empty | The Idempotency-Key was used in the last hour with a different request, or the first request with this key is still running. | Send the first request again, unchanged (the same body, byte for byte) and with the same key. If it is still running you get a 409 again: wait a few seconds and repeat. Otherwise you get its saved answer: a 200 means the batch exists; a 400 means nothing was created, so send the changed request with a new key. |
429 | errorCode RATE_LIMIT_EXCEEDED | Your API key sent too many requests (rate limits). Creating a batch does not call the bank, so this is never the bank's limit. | Wait for the Retry-After interval, then retry with the same Idempotency-Key. |
503 | {"message": "Bank is under maintenance."} | Live mode only: the bank has a maintenance window. | Send the batch again after the window ends. |
500 | No errorCode | An error at Acme. In rare cases (a database connection lost while the batch was being saved) the batch may still have been created. | Wait a minute, then check List Batch Payments for a batch created at that time. If there is none, retry with the same Idempotency-Key. If it fails again, contact Acme with the time of the request. |
If you get no response at all (a timeout), send the same request again (the same body, byte for byte) with the same Idempotency-Key within an hour: you get the original answer, and no second batch is created. If that retry gets a 409, the first request is still running: wait a few seconds and retry again with the same key.
List Batch Payments
Get a Batch Payment
Additions to the Payment API
In addition to the existing Payment fields documented on the Payment API documentation, the following fields will be added for Payments which are part of a Payment Batch.
{
"id": "pymt_XYZ",
// ... existing fields ...
"batchId": "pymtb_XYZ",
"paymentDate": "2024-05-10", // as instructed by customer
"actualPaymentDate": "2024-05-12", // if changed by bank
"bankChargeAmount": 100, // if bank charges present (e.g. for TT / MEPS)
"bankChargeCurrency": "SGD"
}Webhooks
As file-based integration is an inherently asynchronous process, Acme will emit webhooks to inform you of the status of the payments.
- Payment Batch Submitted (sample) - sent after the file is submitted to the bank
- Payment Batch Rejected (sample) - sent if the entire batch is rejected
- Payment Succeeded (sample) - sent for the payments after positive ACK3 (result from clearing system) is received from the bank
- Payment Failed (sample) - sent for the payments after a rejection at the ACK1 (entire batch), ACK2 (bank validation), or ACK3 (clearing system result) level.
After Acme accepts a batch
A 200 means Acme has created the batch, not that the bank has paid. Read each payment's own status:
PROCESSING: Acme has not sent the file to the bank yet. If a batch staysPROCESSINGfor more than an hour, contact Acme. Do not send the batch again, or the payments may be made twice.SUBMITTED: the file is at the bank. Some banks need your approvers to release the file in the bank's portal, and some send no further status after approval or after payment. Then the payment staysSUBMITTEDin Acme: check the bank's portal or your bank statement. Never send aSUBMITTEDpayment again.FAILED: the bank rejected the payment.underlyingErrorMessageholds the bank's reason, when the bank gives one. If the bank rejected the whole file, the batch isFAILEDtoo, with the reason on the batch, and every payment in it isFAILED.COMPLETED: the bank has confirmed the payment.
A batch is COMPLETED once none of its payments is waiting for the bank. That does not mean every payment was made: some may be FAILED.
Test mode
You can use your test API key to test your integration before going live. Test mode validates the input parameters and always assumes success, unless one of the special account numbers below are used.
To test failure cases please use the following account numbers (any bank is fine):
000000000fail this individual payment000000001fail this individual payment000000002fail the entire batch containing this payment
Test mode will emit the following webhooks for a batch that does not fail entirely:
payment-batches.submitted(sample)payments.succeededand/orpayments.failed(sample) for the payments in the batch
If the 000000002 account number is used in a batch, test mode will simulate a failure for the entire batch:
payment-batches.submitted(sample)payment-batches.rejected(sample)payments.failed(sample) for all payments in the batch
Sample Timeline
As the payment is processed, it goes through several stages of validation by Acme and the bank. A payment may be rejected by any of these stages. A payment is considered completed only when the final stage succeeds.
- Client submits 10 payments in the batch, 2 of them are obviously invalid e.g. bank code has wrong format (caught by Acme)
- Acme returns 400 bad request, no batch object is created.
- Client submits 10 payments, fixing the invalid ones.
- Acme returns 200 OK, batch object is created (status PROCESSING), payments are created with status PROCESSING.
- Acme processes the batch and sends a batch of 10 payments to the bank.
- Batch status change to SUBMITTED, 10 payments status change to SUBMITTED.
- Acme sends payment batch submitted webhook for the batch.
- Bank reads the file, validates batch parameters, and delivers ACK1 accepting the batch.
- In case of a rejection in ACK1 (batch rejection), Acme will send a payment batch rejected webhook for the batch, and payment rejected webhook for the payments inside of it.
- Bank processes the file and validates each payment inside. It delivers ACK2 where 2 of the 10 payments are further rejected, while the remaining 8 payments move on to the next stage.
- If any of the payments have ACWC (Accepted With Changes) status, Acme will set actualPaymentDate to the changed value.
- Acme sends payment failed webhook for the 2 payments (payment status change to FAILED).
- Bank submits the payment to the clearing system and delivers their response in ACK3, 8 remaining payments are successful.
- If any of the payments have ACWC (Accepted With Changes) status, Acme will set actualPaymentDate / bankChargeAmount / bankChargeCurrency to the values provided by the bank.
- Acme sends payment succeeded webhook for the 8 payments (payment status change to COMPLETED).