Create a batch
Creates a batch from the same input as POST /batches/validate and validates it again on the server.
With skip_invalid_rows: true (what “Send 36 payments” does after the warning) invalid rows are recorded
in skipped_rows and never paid; with false (the default) any invalid row fails the request with
422 batch_row_invalid and details.rows.
Step-up: a customer session must send X-Step-Up-Token (“Enter the 6-digit code … to send 36 payments
totalling ₦6,480,000.00”).
One hold is placed for the total of the valid rows plus fees (422 insufficient_balance otherwise),
and each valid row becomes a transfer with batch_id and batch_line. If the batch total is over the
caller’s approval threshold the batch and all its transfers are pending_approval with one approval of kind
batch (201); otherwise the batch is processing (202) and its transfers are sent. Each row then
succeeds or fails on its own and fires its own payment.* webhooks. The batch is completed once every row is
final, with status_reason: some_items_failed if any row failed.
Agent tokens can’t create batches (403 role_not_permitted).
Error codes: invalid_api_key, parameter_invalid, parameter_missing, resource_not_found, role_not_permitted, scope_missing, step_up_required, csrf_token_invalid, idempotency_key_reused, batch_row_invalid, insufficient_balance, bank_unavailable, business_frozen, business_not_approved, batch_too_large, limit_exceeded, rate_limited.
Authorizations
Server API key, scoped. pk_test_ keys hit the sandbox tenant.
Headers
Required on money-moving POSTs. Same key + same body replays the first response; same key + different body returns 409 idempotency_key_reused. Kept 7 days.
1 - 255Step-up proof (2FA within the last 5 minutes, from POST /v1/auth/step-up). Required when the caller is a
customer session and the request would send money to someone else (payouts, batches, scheduled payments);
missing or stale returns 403 step_up_required. Not used for own_account transfers, API keys or agent tokens.
Same header as the shared StepUpToken parameter, which is unconditionally required.
Optional client correlation id, up to 128 characters. It is not echoed: the response's X-Request-Id is always
our own req_… id. We log your value with the request as client_request_id, so support can find a request by
either id. A longer value is refused with 400 parameter_invalid.
128Body
Multipart body for validate and create with a CSV file.
Response
Created and waiting for approval (pending_approval, with approval).
A Pay many batch. Its rows are transfers with batch_id.
bat_…
Always batch.
"batch"Lifecycle shared by every money-moving resource (CONVENTIONS §8). UI labels: completed → "Settled", pending_approval → "Pending approval".
draft, pending_approval, scheduled, processing, completed, failed, rejected, canceled, reversed Enough of a PanoFlow account to show it ("Naira · Main · ••••6789"). Full account in the Accounts API.
Sum of the rows being paid.
Sum of the rows' fees.
How many of the batch's rows are in each state.
Who did something. Shown in activity, approvals and the audit log.
RFC 3339, UTC.
"2026-10-27T08:41:00Z"
RFC 3339, UTC.
"2026-10-27T08:41:00Z"
Sub-state of a batch's status. Clients must tolerate new values.
awaiting_approval, sending, all_items_completed, some_items_failed, all_items_failed, rejected_by_approver, canceled_by_user, approval_expired File name, or "Pasted rows".
Rows that were skipped and will never be paid. Returned on GET /batches/{id}; omitted in lists.
The batch's approval, when it needed one.
Why it was made (shown to the approver).
When the last row became final.
"2026-10-27T08:41:00Z"
Up to 20 string keys (≤ 40 chars) with string values (≤ 500 chars). Returned on the resource and its events.