Skip to content

A batch submits up to 1,000 payouts in one request, validates every line, then waits for you to confirm or cancel. Batch payouts covers when to use one and its limits.

  1. Submit the run with POST /payments/organizations/{orgId}/payouts/batches.
  2. Watch it with GET …/batches/{batchId}.
  3. Review the lines with GET …/batches/{batchId}/items.
  4. Confirm with POST …/batches/{batchId}/confirm, or cancel.
  5. Join each created payout to your ledger.
  1. A 202 means received, not paid. Each line of items is a POST /payouts body.

    POST /payments/organizations/{orgId}/payouts/batches
    x-api-key: avvio_live_…
    Idempotency-Key: payroll-2026-09-01-run1
    Content-Type: application/json
    {
    "externalReferenceId": "payroll-2026-09-01",
    "autoCommit": true,
    "items": [
    { "amount": "200.00", "destinationAccountId": "sbx_acct_MXN_4471_ae66cbc5", "reference": "PAYROLL-2026-0042" },
    { "amount": "150.00", "destinationAccountId": "sbx_acct_MXN_0002_1b02c77d", "reference": "PAYROLL-2026-0043" }
    ]
    }
    {
    "batchId": "cmf3k2xg00009q8b7v0w2x4yz",
    "externalReferenceId": "payroll-2026-09-01",
    "status": "received",
    "autoCommit": true,
    "counts": { "received": 2, "invalid": 0, "validated": 0, "creating": 0,
    "created": 0, "create_failed": 0, "canceled": 0, "requires_review": 0 },
    "createdAt": "2026-09-01T14:03:11.000Z",
    "updatedAt": "2026-09-01T14:03:11.000Z",
    "completedAt": null
    }

    An unknown field on any line refuses the whole request with 400. A throttled submit gets 429 with Retry-After in seconds; resend with the same key.

    Idempotency-Key identifies the HTTP attempt: the same key and body within 7 days returns the same batch, with the Idempotency-Replayed header (Idempotency). externalReferenceId identifies the run permanently, so it also catches a job that loses its key and re-runs the file under a fresh one. It is required, 1 to 128 characters of letters, digits, spaces and . _ : -, and unique per organization. Submit accepts X-Allow-Duplicate: true, but the run id must still differ. A second batch with the same id answers:

    // 409
    {
    "type": "PAYOUT_BATCH_DUPLICATE_REFERENCE",
    "message": "A batch with externalReferenceId \"payroll-2026-09-01\" already exists (cmf3k2xg00009q8b7v0w2x4yz). NOTHING WAS SUBMITTED. …",
    "originalBatchId": "cmf3k2xg00009q8b7v0w2x4yz"
    }

    If this was a retry, continue from originalBatchId. A new run, such as corrected lines, needs its own id (payroll-2026-09-01-r2). GET …/batches?externalReferenceId= finds the run an id already names.

    With true (the default), a run with no validation errors goes straight to creation and a run with any error holds at awaiting_confirmation. With false, it always holds. When approvals are required, autoCommit is forced off and confirm captures the approval.

  2. GET /payments/organizations/{orgId}/payouts/batches/{batchId}
    {
    "batchId": "cmf3k2xg00009q8b7v0w2x4yz",
    "externalReferenceId": "payroll-2026-09-01",
    "status": "awaiting_confirmation",
    "autoCommit": true,
    "counts": { "received": 0, "invalid": 3, "validated": 247, "creating": 0,
    "created": 0, "create_failed": 0, "canceled": 0, "requires_review": 0 },
    "estimatedSourceTotal": "49400.00",
    "createdAt": "2026-09-01T14:03:11.000Z",
    "updatedAt": "2026-09-01T14:03:40.000Z",
    "completedAt": null
    }

    estimatedSourceTotal is the advisory sum of the valid lines, to compare with your balance before confirming. It is null while validating and when any line uses amountLeg: "destination".

    A batch tracks creation, not settlement (batch statuses). On a failed batch, error is an object, { "code": "VALIDATION_RUN_FAILED", "message": "…" } (read error.code), and no payout exists; contact support with the batchId instead of resubmitting. Instead of polling, listen for the payout_batch.* webhooks. List your runs, newest first:

    GET /payments/organizations/{orgId}/payouts/batches?status=&externalReferenceId=&limit=&cursor=

    limit is 1 to 100 (default 50). A foreign cursor or unknown status is a 400, never an empty page.

  3. GET /payments/organizations/{orgId}/payouts/batches/{batchId}/items?status=invalid

    Lines come back in order, each echoing its instruction:

    {
    "data": [
    {
    "index": 17,
    "status": "invalid",
    "instruction": { "amount": "200.00", "destinationAccountId": "sbx_acct_MXN_0009_d41d8cd9", "reference": "PAYROLL-2026-0059" },
    "errors": [ { "code": "DESTINATION_ACCOUNT_NOT_FOUND", "message": "…" } ]
    }
    ],
    "hasMore": false,
    "nextCursor": null
    }

    limit is 1 to 1,000 (default 100); the cursor is the last line’s index. Error codes match a single POST /payouts (Errors).

    ?format=csv returns the whole run, filtered by ?status=, with columns index, status, payoutId, amount, amountLeg, destinationAccountId, reference, errorCode, errorMessage (first error per line only).

    Status Meaning Terminal
    received Stored, not yet examined
    invalid Failed validation; see errors. No payout ✓
    validated Waiting for creation or your confirm
    creating A quote/accept is in flight
    created A payout exists; payoutId is set ✓
    create_failed Refused at creation; see errors. No payout ✓
    canceled Batch canceled before this line was attempted ✓
    requires_review Outcome unknown; see below ✓
  4. Both take an Idempotency-Key, and both act only before the run is committed.

    POST /payments/organizations/{orgId}/payouts/batches/{batchId}/confirm
    Idempotency-Key: <a uuid>

    Confirm works only in awaiting_confirmation, otherwise 409 PAYOUT_BATCH_NOT_CONFIRMABLE. Valid lines go to creation; resubmit fixed invalid lines as a new batch with a new externalReferenceId.

    With approvals required, the first confirm answers 202 with one approval for the whole run:

    { "status": "pending_approval", "approvalId": "cmf3k2xf90008q8b7q4r6s8tu", "requiredApprovals": 2, "expiresAt": "…" }

    At quorum the run is released within a minute. A repeat confirm returns the same approval; after a rejection it is 409 PAYOUT_BATCH_AWAITING_APPROVAL, and cancel still works (Approvals).

    POST /payments/organizations/{orgId}/payouts/batches/{batchId}/cancel
    Idempotency-Key: <a uuid>

    Cancel works in received, validating or awaiting_confirmation, so a canceled batch never produces a payout; later it is 409 PAYOUT_BATCH_NOT_CANCELABLE. A created payout can be canceled with POST …/payouts/{payoutId}/cancel only while it waits on your own funding.

  5. GET /payments/organizations/{orgId}/payouts/batches/{batchId}/items?status=created

    Each created payout behaves like one sent alone: it appears in GET /orders, emits payout.* events, and can still fail or be returned (Track status & failures). Reconcile through the event feed.

Was this page helpful?