UTXO mass payouts
Mass payouts for BTC, LTC, and DOGE.
UTXO mass payouts support Bitcoin, Litecoin, and Dogecoin. payzum derives a unique HD deposit address per order, waits for your funding transaction, then signs and broadcasts batched payout transactions on your behalf.
See Mass payouts overview for the CSV format and quote field reference.
Order lifecycle
pending_quote → pending_deposit → underfunded | funded → executing
→ completed | partial_failed | expired | cancelled | refunded
| Status | Description |
|--------|-------------|
| pending_quote | Order created; quote computed. Call confirm to proceed. |
| pending_deposit | Quote acknowledged; waiting for on-chain deposit. |
| underfunded | Expiry elapsed with a partial deposit received. No payout made. |
| funded | Full deposit confirmed by the chain watcher. |
| executing | Batches being broadcast. |
| completed | All batches confirmed. Settlement CSV available. |
| partial_failed | Some batches failed permanently. Operator retry required. |
| expired | Expiry elapsed with no deposit received. |
| cancelled | Cancelled by merchant before funding. |
| refunded | Partial funds returned after an underfunded or failed order. Applied manually by payzum operations — there is no /refund endpoint on UTXO. |
Endpoints
All endpoints require the x-api-key header with your merchant API key. Pagination, filters, the error envelope, and the Idempotency-Key semantics follow the shared conventions in the Mass payouts overview.
| Method | Path | Description |
|--------|------|-------------|
| POST | /v1/mass-payout | Create order (multipart form; optional Idempotency-Key header) |
| GET | /v1/mass-payout | List orders (paginated; filter by status, chain) |
| GET | /v1/mass-payout/:id | Fetch order with batch detail |
| GET | /v1/mass-payout/:id/recipients | Paginated recipient rows |
| GET | /v1/mass-payout/:id/report.csv | Settlement CSV (available at terminal state) |
| POST | /v1/mass-payout/:id/confirm | Confirm quote; advance to pending_deposit |
| POST | /v1/mass-payout/:id/cancel | Cancel order (idempotent) |
| POST | /v1/mass-payout/:id/refresh-quote | Recompute network fee and extend expiry |
POST /v1/mass-payout/:id/retry-failed also exists but is operator-only: it is authenticated with the X-Operator-Key header and used internally by payzum operations to retry failed batches. It is not a merchant endpoint. There is no /refund endpoint on UTXO — the refunded lifecycle state is applied manually by operations.
Order object
GET /v1/mass-payout/:id returns { "ok": true, "order": { ... } } with batches nested inside order (unlike EVM, where order and batches are siblings). Order fields:
id, status, chain, mode, depositAddress, expiresAt (epoch ms or null), totalRecipients, totalBatches, quote — with sumRecipientsRaw, networkFeeRaw, payzumFeeRaw, totalToSendRaw (integer strings in the smallest unit) plus payzumFeeBps and feeRateSatVbSnapshot (numbers) — batches[], fundedAt, receivedAmountRaw, depositTxids, completedBatches, failedBatches, completedAt, cancelledAt, failureReason, createdAt.
In list responses (GET /v1/mass-payout) each order's batches array is always empty; only the fetch-by-id endpoint populates it.
Create order — request fields
POST /v1/mass-payout accepts multipart/form-data.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| chain | string | yes | bitcoin | litecoin | dogecoin |
| mode | string | yes | mainnet | testnet |
| csv | file | yes | CSV file with header row; columns address, amount, optional label |
Refresh quote
POST /v1/mass-payout/:id/refresh-quote recomputes the network fee estimate and extends the order expiry to min(now + 24h, createdAt + 7d). Only valid while status is pending_quote or pending_deposit and the deposit has not yet been received. Use this when fee rates have changed since order creation.
Error catalog
Errors use the { "ok": false, "error": { "kind": ... } } envelope (auth and rate-limit failures use the canonical {statusCode, code, message} envelope instead — see the overview).
| Error kind | HTTP | Description |
|------------|------|-------------|
| invalid_csv | 400 | Malformed form data, missing field, or CSV parse failure |
| invalid_address | 400 | A recipient address failed chain-specific validation (rowIndex and address in the error body) |
| over_max_decimals | 400 | Amount precision exceeds chain maximum |
| dust_below_threshold | 400 | A recipient amount is below the chain dust limit |
| order_too_small | 400 | Total payout sum below the chain minimum order size |
| chain_disabled | 400 | Chain is disabled via the operator kill-switch (on EVM this same kind is returned as 409) |
| idempotency_conflict | 409 | Duplicate (merchant, Idempotency-Key). The original order is not replayed — fetch it with GET /v1/mass-payout/:id |
| invalid_transition | 409 | The order is not in a state that allows the requested action |
| vexo_register_failed | 422 | Deposit-address registration with the watcher failed |
| derivation_exhausted | 422 | No deposit address could be derived |
| broadcast_failed | 422 | A payout transaction could not be broadcast |
| sign_failed | 422 | A payout transaction could not be signed |
| not_found | 404 | Unknown order id for this merchant |
| rate_limited | 429 | Shared 60/min per-merchant rate limit exceeded |
| internal | 500 | Unexpected server error |
Create order — cURL example
curl -X POST "$PAYZUM_BASE/v1/mass-payout" \
-H "x-api-key: $PAYZUM_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "chain=bitcoin" \
-F "mode=mainnet" \
-F "csv=@payouts.csv"The response includes the order object with depositAddress and quote.totalToSendRaw. Fund the deposit address with exactly totalToSendRaw satoshis (or litoshis / koinus for LTC / DOGE) to advance the order.
Confirm order — cURL example
curl -X POST "$PAYZUM_BASE/v1/mass-payout/pzmpo_abc123/confirm" \
-H "x-api-key: $PAYZUM_API_KEY"Returns the updated order with status: "pending_deposit".
Poll for completion
curl "$PAYZUM_BASE/v1/mass-payout/pzmpo_abc123" \
-H "x-api-key: $PAYZUM_API_KEY" | jq '.order.status'Poll until status is completed, partial_failed, expired, or cancelled. Subscribe to Mass-payout webhooks to avoid polling.
Download settlement report
curl -o settlement.csv \
-H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/mass-payout/pzmpo_abc123/report.csv"The report is available once the order reaches completed or partial_failed. It includes per-recipient status, batch index, and transaction IDs.