Payouts

Disburse to many destinations in one batch. Submitted async (202), executed item-by-item, partial-failure tolerant: one bad item never sinks the batch. completed, partially_completed, or failed.

A payout is a batch of disbursements: one call, many destinations, executed item-by-item. It's how you pay out a marketplace's sellers, a payroll run, a round of creator earnings, without firing N separate calls and hand-rolling your own retry loop. Acute runs the batch asynchronously and, crucially, is partial-failure tolerant: one item bouncing doesn't fail the other ninety-nine.

Submit the whole batch at once:

# batch payout
curl -X POST https://sandbox.api.acute.network/v1/payouts \
  -H "Authorization: Bearer acuinf_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "destinationWalletId": "acuinf830192847561wlt", "amount": 500000, "reference": "seller-1" },
      { "destinationWalletId": "acuinf771204938651wlt", "amount": 300000, "reference": "seller-2" }
    ]
  }'

The call returns 202 Accepted, not 201. The batch is validated (funds checked, every destination must be tier1) and enqueued; the items run on a worker. You get the payout back as processing immediately, with every item pending.

202 Accepted
json
{
  "success": true,
  "statusCode": 202,
  "data": {
    "id": "acuinf093817264550pyo",
    "sourceWalletId": "acuinf601928374651wlt",
    "totalAmount": 800000,
    "totalFee": 12000,
    "itemCount": 2,
    "status": "processing",
    "items": [
      {
        "id": "acuinf093817264551poi",
        "amount": 500000,
        "fee": 7000,
        "status": "pending",
        "counterparty": { "accountNumber": "0123456789", "accountName": "Ada Lovelace", "bankCode": "000013" },
        "failureReason": null
      },
      {
        "id": "acuinf093817264552poi",
        "amount": 300000,
        "fee": 5000,
        "status": "pending",
        "counterparty": { "accountNumber": "0987654321", "accountName": "Grace Hopper", "bankCode": "000014" },
        "failureReason": null
      }
    ],
    "currency": "NGN",
    "createdAt": "2026-06-26T12:00:00.000Z"
  }
}

202, not 201

Payout creation is the one money endpoint that returns 202 Accepted, because the work is genuinely asynchronous: the batch hasn't run yet. Don't read item statuses off the create response and call it done; they're all pending there. Poll GET /payouts/:id or, better, wait for the payout.completed / payout.partially_completed webhook.

Every payout item disburses on the bank rail: same posting and same non-lossy resolver as a withdrawal. When the worker executes an item, it posts the hold, fires the NIP, and lets the resolver confirm. The fee per item is the withdrawal clamp (clamp(₦5, 1%, ₦180)) plus the flat ₦20.00 NIP charge from the provider.

One payout item executes

₦5,000.00 to a seller · ₦50.00 Acute fee · ₦20.00 flat provider fee

ledger_transaction kind: payout_item, one per item. Each posts independently: the batch's totalAmount and totalFee are just the sum of its items.

A failed item is reversed exactly like a returned withdrawal: a reversing ledger transaction re-credits the source wallet. The batch's successCount / failureCount move accordingly, and one item's failure leaves every other item untouched.

A payout's status is a rollup of its items. While any item is still pending or processing, the batch is processing. Once they've all resolved, the batch lands on one of three terminals.

processingentry status

The batch is running. At least one item is still pending or processing; the rollup stays here until every item resolves.

Transitions out

  • processingcompleted

    All items resolve to completed

    firespayout.completed

  • processingpartially_completed

    Items resolve to a mix of completed and failed

    firespayout.partially_completed

  • processingfailed

    All items resolve to failed (every hold reversed)

The rollup rule, exactly:

  • any item still pending/processing → batch processing
  • all items completed → batch completedpayout.completed
  • all items failed → batch failed
  • otherwise (a mix) → batch partially_completedpayout.partially_completed

Each item has its own status (pending, processing, completed, or failed), so when a batch comes back partially_completed, you read the items[] to see which seller didn't get paid and why (failureReason).

partially_completed is a success-ish status, so read the items

partially_completed means the batch finished and some money moved. It is not an error you can ignore: the failed items were reversed to your source wallet, but the intended recipients didn't get paid. Inspect items[], surface the failures, and re-pay the failed ones (a fresh payout, fresh Idempotency-Key).

webhook: payout.partially_completed
json
{
  "id": "acuinf774012398561evt",
  "type": "payout.partially_completed",
  "createdAt": "2026-06-26T12:03:20.000Z",
  "data": {
    "id": "acuinf093817264550pyo",
    "status": "partially_completed",
    "totalAmount": 800000,
    "itemCount": 2
  }
}

  • Source defaults to settlement. Omit sourceWalletId and the batch draws from your org's settlement wallet. Point it at any tier1 wallet you control.
  • Funds checked up front. The batch validates that the source can cover the total (amounts + fees) before enqueueing. Insufficient funds → the create call fails; nothing runs half-paid.
  • Every destination must be tier1. A kycStatus: none destination fails validation: fix KYC before the batch, not after.
  • Idempotency-Key required. Resubmitting the same batch with the same key returns the same payout: you won't double-disburse a payroll because a request timed out.