What does the flow look like end to end?
The whole flow is a Portal feature performed in your browser session; there is no batch endpoint on the
API-key-signed integration API — for an integration, use the single refund API instead. Confirming produces a
Refund Batch ID shaped
BRP + MID + YYYYMMDD + a 6-digit daily sequence, e.g.
BRP<your merchant ID>20260225000001; the batch’s Batch Name is the file name you uploaded, so name files
something you will recognise later.
What must the file look like?
Hard limits:
.csv only, at most 2 MB, at most 2,000 records, non-empty and parseable — the Portal checks
extension and size before uploading, the server checks both again. Two things catch people out: delete the
sample row (a row carrying the template’s example order id is rejected on its own, before any other check), and
one currency per file.
Why was my whole file rejected?
Upload validation is all-or-nothing: nothing is created, and the Portal lists offending rows as row, field, message. Checks run in a fixed order, which is why you often see only one category at a time:- File-level checks — format, size, emptiness, duplicate content, a batch already in progress.
- The sample-row check; if it fires, it is the only error returned.
- Per-row checks — order exists under your MID, no duplicate order inside the file, amount > 0, currency valid, consistent, and equal to the order’s currency.
- The single-refund preconditions per row — order paid and refundable, no refund already in progress, inside the refund window, amount within the remaining refundable amount, no dispute lock (Refund FAQ explains each).
- The batch balance check, only if every row passed — the file’s total must be covered by the same refund funding source a single refund would use, or you get one batch-level error with no row number (“Insufficient merchant refund account capacity for batch refund”).
Why can’t I upload — duplicate file, or a batch in progress?
Duplicate detection works on file content, not name, scoped to your MID. Re-uploading the same content while an earlier upload is still awaiting confirmation returns that same pending upload, so you can re-open the confirm dialog; once a batch has been created from that content, the content is rejected permanently — to retry failed rows build a new file, and any change, even the reason text, makes it a new file. A validated upload also expires if left sitting: confirming later fails with File token is invalid or expired, and you upload again. Separately, upload and confirm both refuse while any refund in any of your batches is still in progress (“You have a batch refund in progress. Please wait for it to complete.”), so plan large runs as a queue of files; single refunds via the API are not blocked by this. And because confirm re-validates every row, a batch left sitting in the confirm dialog while things changed — an order refunded elsewhere, a window closed, the balance moved — fails at confirm with the first offending row’s message instead of creating a partly invalid batch.What do the batch statuses mean?
Status is derived from the row counts rather than tracked separately, so it moves on its own as rows settle.
Total / Success / Failed / Processing Count are shown per batch: Failed Count includes rows that failed
validation at submit time, Processing Count rows not yet started. One bad row does not stop the batch — rows are
processed independently, which is what
PARTIAL_SUCCESS means, and a batch has no retry action: put the failed
rows in a new file, or refund them one at a time.
Where do I see which rows failed and why?
Click Detail on a batch: it opens the Refunds tab pre-filtered by that Refund Batch ID, carrying the batch page’s date range. Each refund is then a normal record — its detail page shows the Refund Batch ID, and the failure reason when the status is Refund Failed. The Refunds tab also has a Refund Batch ID column and filter, so you can reach the same view directly. The batch list export (Batch_Refunds_<timestamp>.csv) is a batch-level summary — ids, name, amount, currency,
status, the four counts, operator, created and updated times — and does not contain per-row failure reasons;
times in it use the UTC offset your browser reports. The list itself filters on Refund Batch ID, status, currency
and created datetime, and opens on the last three months. Rarely a row fails so early that no refund record is
created: it still counts in Failed Count with nothing to open in the Refunds tab — contact support with the
Refund Batch ID for those.
How does this relate to the single refund API?
Same engine, different entry point: identical lifecycle, account selection and settlement treatment. Two practical differences. Refund request id — batch rows get a system-generated one you never see, so track them by Refund Batch ID or by the refund order id in the Portal, not by your own reference. Notifications — batch rows carry no per-row notify URL, so refund notifications go to the default refund notification URL configured for your account; with none configured, batch rows send no webhook and the Portal is your only view.A file that keeps failing? Contact support with it and, if one was created, the Refund Batch ID.