Payouts
A payout sends USDC from the linked wallet to any 0x address on Base. By default the user approves every payout in their wallet. If you pass a signed mandate that has enough budget left, the payout goes ahead without a separate approval.
WARNING
Payouts on-chain are final. Check the recipient address and amount, and confirm them with the user, before sending.
payout
# Standard: the user approves via approvalUrl
fluxa-wallet payout --to 0x1234567890abcdef1234567890abcdef12345678 --amount 1000000 --id pay_001
# Within a signed mandate: no separate approval
fluxa-wallet payout --to 0x1234567890abcdef1234567890abcdef12345678 --amount 1000000 --id pay_002 \
--mandate mand_xxxxxxxxxxxxx --biz-id order_20261027_001 --description "Refund for order #001"| Flag | Type | Required | Default | Description |
|---|---|---|---|---|
--to | 0x + 40 hex characters | Yes | — | Recipient address |
--amount | integer, atomic units | Yes | — | Amount. USDC has 6 decimals: 1000000 = 1.00 USDC, 10000 = 0.01 USDC |
--id | string | Yes | — | Your unique payout ID, which doubles as the idempotency key |
--network | string | No | base | Network |
--asset | address | No | USDC on Base | Token contract address |
--mandate | string | No | — | Signed mandate to pay within, which skips the approval step |
--biz-id | string | No | — | Your business reference (e.g. order ID). Can't be reused while a payout with the same value is still active |
--description | string | No | — | Human-readable note stored with the payout |
Standard payout: awaiting approval
{
"success": true,
"data": {
"payoutId": "pay_001",
"txHash": null,
"approvalUrl": "https://wallet.fluxapay.xyz/authorize-payout/pay_001",
"expiresAt": 1761586400
}
}Send the user the complete approvalUrl as a clickable link, then poll payout-status.
Payout within a mandate: approvalUrl is null and the payout is already processing. Poll payout-status until it finishes.
Idempotency
Re-sending the same --id returns the existing payout instead of creating a second one, so retrying after a timeout is safe. Use a new --id for each new payout.
| Error | What to do |
|---|---|
Missing required parameters: --to, --amount, --id | Supply all three |
Invalid recipient address format | Use a full 0x… address (40 hex characters) |
Amount must be a positive integer (smallest units) | Convert to atomic units. No decimals |
| Error mentioning the mandate | The mandate isn't signed, has expired, or doesn't have enough budget left. Check it with mandate-status, or leave out --mandate so the user approves this payout directly |
payout-status
fluxa-wallet payout-status --id pay_001| Flag | Type | Required | Description |
|---|---|---|---|
--id | string | Yes | The payout ID you used with payout |
Prints { "success": true, "data": { ... } }. The payout record includes payoutId, displayStatus, txHash (once on-chain), amount, toAddress, network, and timestamps.
Reading the status
Poll until the payout reaches a final outcome, and read displayStatus:
displayStatus | What to do |
|---|---|
awaiting_approval | The user hasn't approved yet. Remind them of the approvalUrl |
processing | Being sent. Keep polling, and don't create a second payout |
completed | Done. txHash is the on-chain transaction |
failed, expired, rejected, canceled | Not sent. Tell the user; create a new payout with a new --id if they still want to send |
Treat any other value as processing. The record also has a lower-level status field; don't build logic on it, because its values can change.
while true; do
S=$(fluxa-wallet payout-status --id pay_001 | jq -r '.data.displayStatus // .data.payout.displayStatus')
case "$S" in
completed) echo "completed"; break ;;
failed|expired|rejected|canceled) echo "not sent"; break ;;
awaiting_approval) echo "awaiting approval" ;;
*) echo "processing" ;;
esac
sleep 5
doneSee also: Payout guide.
