Batch payments
A payment batch pays many recipients at once: payroll, or a run of supplier invoices. Every recipient is paid by bank transfer in the same currency, and one approval covers the whole batch.
Read Paying out first. A batch uses the same contacts, quotes and approvals as a single payment.
What you can do
Section titled “What you can do”- Build a draft batch from saved contacts, from new recipients described inline, or from a CSV sheet.
- Check every row, and price them all at once.
- Submit the batch for one approval.
- Follow each row’s payment, and retry the ones that failed.
The sequence
Section titled “The sequence”1. Create a draft
Section titled “1. Create a draft”Call createPaymentBatch with the
payout_currency every recipient receives, a payout_reason code shared by
every row, and the rows. A batch holds at most 500 rows.
Each row either names a saved, approved payment_contact_id, or describes a
new recipient inline. amount is in the payout currency: what the recipient
receives. Set the batch’s amount_side to deducting to enter what each row
costs instead. reference is what the recipient’s statement shows, at most 35
characters; without it, the row uses the batch’s statement_description.
curl -s "$ELE_API_BASE/api/v1/organizations/$ORGANIZATION_ID/payment-batches" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY" \ -H 'Content-Type: application/json' \ -d '{ "name": "October payroll", "payout_currency": "SGD", "source_currency": "USD", "payout_reason": "PYR005", "statement_description": "October salary", "rows": [ {"payment_contact_id": "'"$CONTACT_ID"'", "amount": "4200.00"}, { "amount": "3900.00", "reference": "Salary Oct", "recipient": { "account_holder_name": "Tan Mei Ling", "account_type": "individual", "country_alpha2": "SG", "bank_details": {"bank_name": "DBS Bank Ltd", "account_number": "0987654321", "swift_code": "DBSSSGSG"} } } ] }'source_currency is the balance the batch is paid from. You can leave it out
now and set it later with
updatePaymentBatch, but the
batch cannot be priced without it.
To run an earlier batch again, send rerun_of_batch_id to copy all its rows.
To retry a completed batch, send retry_of_batch_id: it copies only the rows
that failed or were cancelled, and those whose payment failed or was returned
after it was sent. Use one or the other.
2. Or import a CSV sheet
Section titled “2. Or import a CSV sheet”To start from a spreadsheet, get the sheet’s columns for the payout currency
from getPaymentBatchTemplate.
csv is a header row ready to save, and each column’s level says whether the
currency’s banks need it.
curl -s "$ELE_API_BASE/api/v1/payment-batches/template?payout_currency=SGD" \ -H "Authorization: Bearer $ELE_TOKEN"Then upload the filled sheet to a draft batch in three calls:
-
createPaymentBatchUploadwith the file’sfilename,content_typeand exactbyte_size. A CSV may be at most 5 MB.Terminal window curl -s "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/upload" \-H "Authorization: Bearer $ELE_TOKEN" \-H "Idempotency-Key: $KEY" \-H 'Content-Type: application/json' \-d '{"filename": "payroll_oct.csv", "content_type": "text/csv", "byte_size": 18234}' -
PUTthe raw file toupload_urlbeforeexpires_at, sending exactly theheadersfrom the response:Terminal window curl -s -X PUT "$UPLOAD_URL" \-H 'content-type: text/csv' \-H 'content-length: 18234' \--data-binary @payroll_oct.csv -
importPaymentBatchFilewith the upload’sidasfile_id:Terminal window curl -s "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/import" \-H "Authorization: Bearer $ELE_TOKEN" \-H "Idempotency-Key: $KEY" \-H 'Content-Type: application/json' \-d "{\"file_id\": \"$FILE_ID\"}"
Each line of the sheet becomes a row. A sheet Ele cannot read is refused with
422 invalid_file, and details.errors says why. A bad value on one line does
not refuse the sheet: it shows up as an issue on that row. Importing again
replaces the rows from the earlier sheet.
3. Fix the rows
Section titled “3. Fix the rows”Each row of a draft has issues. An issue with level set to needs_fix
stops the batch from being priced or submitted; a warning does not. field
names what to fix, and message says it in plain words. The batch’s
needs_fix_count must reach zero.
Change the draft with:
addPaymentBatchRowsto add rows,updatePaymentBatchRowto change one,removePaymentBatchRowto take one out (it stays on the record as removed, and is never paid),updatePaymentBatchto change the batch itself.
curl -s -X PATCH "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/rows/$ROW_ID" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY" \ -H 'Content-Type: application/json' \ -d '{"amount": "4000.00"}'validatePaymentBatch checks
every row again without pricing anything.
4. Price the batch
Section titled “4. Price the batch”reviewPaymentBatch prices every
row. It first saves each inline recipient as a contact that belongs to the
batch, and registers the bank account with Ele’s payout partner.
curl -s -X POST "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/review" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY"Each row gets a disposition. A ready row has a quote; any other value
says why the row cannot be sent:
disposition |
Meaning |
|---|---|
ready |
Priced, with a quote. |
needs_fix |
The row has an issue to fix. |
missing_amount |
The row has no amount. |
currency_mismatch |
The contact’s bank account is in another currency. |
destination_not_ready |
The bank details were refused, or are still being checked. |
contact_not_approved |
The saved contact is not approved yet. |
corridor_closed |
Ele cannot pay from the source currency to this recipient’s currency and country. |
below_minimum |
The amount is below the minimum for this route. |
quote_failed |
Pricing failed. error says why. |
Each row’s destination is the bank account it pays, in the same shape a
payment contact’s destination has. Show it to the person before they approve.
It is null until review saves an inline recipient as a contact.
destination_summary is deprecated; use destination.
The batch then shows total_deducting, the fees, and balance_shortfall if
the source balance does not cover the total.
Each quote expires, as a single payment’s does. Any change to the draft, and any lapsed quote, means you must review again before you submit.
5. Submit for approval
Section titled “5. Submit for approval”submitPaymentBatch turns every
ready row into a payment awaiting approval and raises one approval request for
the batch.
curl -s -X POST "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/submit" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY"Ele refuses to submit unless every row is ready, every quote is live, and the
source balance covers the total. The batch’s status becomes
awaiting_approval, and approval_request_id names the request.
6. Have a person approve it
Section titled “6. Have a person approve it”An owner or admin approves the request with
approveRequest, which needs step-up.
A person must complete it; see
Step-up and approvals.
Approving the batch also approves the new recipients it created.
If any row’s quote has lapsed by then, Ele refuses the approval with
batch_quote_expired, and details.rows lists the row numbers. Re-price them
with requotePaymentBatch, show
the person the new total, and approve again:
curl -s -X POST "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/requote" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY"Rows whose quote is still live keep it. The batch’s expired_row_count tells
you when a requote is needed.
On approval, Ele sets the total aside from the source balance and starts sending the rows.
7. Follow the batch
Section titled “7. Follow the batch”Poll getPaymentBatch until the
batch is completed:
curl -s "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID" \ -H "Authorization: Bearer $ELE_TOKEN"Each row’s result is written once Ele has handled it: sent, failed or
cancelled. sent means the payment was handed over; the row’s
payment_state says where that payment is now, using the same values as a
payment’s status in Paying out. payment_id
names the payment, which you can read with
getPayment. When a row fails, error
holds a code such as insufficient_funds, quote_drift, payment_failed or
payment_reversed.
A row whose payment cannot go out yet is held, not failed: its payment_state
is on_hold, and the batch’s held_count counts it. The batch stays
executing until every held row is sent or resolved.
To pay the failed rows again once the batch is completed, create a new batch
with retry_of_batch_id.
States
Section titled “States”A batch’s status:
status |
Meaning | What you do |
|---|---|---|
draft |
Rows are being added, fixed and priced. | Review, then submit. |
awaiting_approval |
Submitted; waiting for a person. | Ask an approver. Requote if quotes lapse. |
executing |
Approved; rows are being sent. | Poll. |
completed |
Every row has a result. | Check each row. Retry failed rows if needed. |
cancelled |
Cancelled or rejected before it was sent. | Nothing. |
Step-up and approvals
Section titled “Step-up and approvals”Building, reviewing and submitting a batch need neither step-up nor approval.
Approving it (approveRequest) needs step-up, by an owner or admin. One
approval covers every row: the rows have no approval requests of their own.
Who can see a batch
Section titled “Who can see a batch”A draft belongs to the person who started it. Only they can see, change, review, submit or delete it. Once submitted, the organization’s approvers can see it too, and can requote or cancel it.
Cancelling
Section titled “Cancelling”- A draft: delete it with
deletePaymentBatch. - Awaiting approval: cancel it with
cancelPaymentBatch, or reject its approval request. Every row’s payment is cancelled with it. - Executing: an approved batch cannot be cancelled here.
curl -s -X POST "$ELE_API_BASE/api/v1/payment-batches/$BATCH_ID/cancel" \ -H "Authorization: Bearer $ELE_TOKEN" \ -H "Idempotency-Key: $KEY"What to poll
Section titled “What to poll”getPaymentBatchwhile a batch isawaiting_approval(to catch lapsed quotes) orexecuting.listPaymentBatchesfor an organization’s batches, newest first.
Common errors
Section titled “Common errors”| Status | type |
When | What to do |
|---|---|---|---|
404 |
not_found |
The batch is another person’s draft, or does not exist. | Check the ID and who started it. |
409 |
invalid_status |
The call does not fit the batch’s status, such as editing a submitted batch. | Read the batch and decide. |
409 |
batch_modified |
The batch changed while it was being priced. | Read it and try again. |
409 |
not_awaiting_approval |
Cancelling a batch that is not awaiting approval. | Delete a draft instead. |
422 |
source_currency_required |
Reviewing or submitting without a source balance. | Set source_currency. |
422 |
rows_not_ready |
Submitting while some rows are not ready, or their quotes lapsed. details.rows lists them. |
Fix the rows and review again. |
422 |
no_rows |
Submitting a batch with no rows. | Add rows. |
422 |
too_many_rows |
More than 500 rows. | Split the batch. |
422 |
insufficient_funds |
The source balance does not cover the batch, at submit or at approval. | Add money, or remove rows. |
422 |
batch_quote_expired |
Approving with lapsed quotes. details.rows lists them. |
Requote, then approve again. |
422 |
invalid_file |
The CSV cannot be read. details.errors says why. |
Fix the sheet. |
422 |
file_not_uploaded |
Importing before the PUT finished. |
Upload the file first. |
422 |
payout_reason_not_found |
The payout_reason code is unknown. |
Use a code from getPaymentsConfig. |
For other types, see Errors.