Skip to content

Payout

What it is

A Payout lets an agent send USDC from the user's FluxA Wallet to any external address on Base. Common use cases include withdrawals, revenue splits, paying contractors, refunds, and settling off-chain obligations.

Payouts support two approval paths:

  • Standard flow — the agent initiates, the user approves in the FluxA Wallet UI, and the system signs and broadcasts.
  • Mandate auto-approval — if the agent passes a pre-signed mandateId with sufficient budget, the payout skips manual approval and enters authorized directly.

What agents can do with it

  • Send funds to any address — initiate outbound USDC transfers to any 0x address on Base.
  • Auto-approve with mandates — pass a mandateId to skip user approval when the mandate covers the amount.
  • Deduplicate by business ID — pass a bizId so the same external order can never create two active payouts.
  • Track payout status — poll the status endpoint until the payout reaches a terminal state (confirmed or failed).
  • Attach metadata — include custom metadata (order IDs, source labels) for reconciliation.
  • Webhook notifications — provide a webhook URL to receive push notifications on completion.
  • Idempotent retries — reuse the same payoutId to safely retry without duplicate transfers.
MethodEndpointDescription
POST/api/payoutsCreate a payout request
GET/api/payouts/:payoutIdGet payout status

User-side actions (approve / deny a pending payout) are performed by the user directly in the FluxA Wallet UI via the approvalUrl returned at creation; there is no public API for them.

Integration flows

There are two flows depending on whether the agent has a pre-signed budget mandate.

Flow A — with a budget mandate (auto-approval)

Use this when the agent should send payouts autonomously within a user-approved budget (e.g. recurring refunds, revenue splits, task rewards). The payout skips manual user approval and starts broadcasting immediately.

text
** Your task **
Send 2.00 USDC to 0xABCD...1234 as a reward for task #42.

** Step 1 — Create a budget mandate (once, reused across payouts) **
If the agent does not already have a signed mandate that covers this
kind of payout, create one and have the user sign it:

curl -X POST https://walletapi.fluxapay.xyz/api/mandates/create-intent \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_JWT" \
  -d '{
    "intent": {
      "naturalLanguage": "Pay data labeling task rewards",
      "currency": "USDC",
      "limitAmount": "100000000",
      "validForSeconds": 2592000,
      "category": "task-reward"
    }
  }'

The response returns:
- mandateId (e.g. "mnd_abc12345")
- authorizationUrl — surface to the user so they can sign in the
  FluxA Wallet. The mandate is not usable until signed.

Poll the mandate until status is "signed":
  curl https://walletapi.fluxapay.xyz/api/mandates/agent/mnd_abc12345 \
    -H "Authorization: Bearer $AGENT_JWT"

** Step 2 — Create the payout with mandateId **
Pass the signed mandateId; the payout auto-approves atomically if
the remaining budget covers the amount:

curl -X POST https://walletapi.fluxapay.xyz/api/payouts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_JWT" \
  -d '{
    "payoutId": "reward-task-42",
    "toAddress": "0xABCD...1234",
    "amount": "2000000",
    "mandateId": "mnd_abc12345",
    "bizId": "task_42_reward",
    "description": "Data labeling reward — task #42"
  }'

Expected response:
- status: "authorized"  (auto-approved, no user interaction)
- approvalUrl: null
- mandateId: "mnd_abc12345"

If the mandate budget is insufficient, the call fails with
`mandate_insufficient_budget`; fall back to Flow B or top up the
mandate.

** Step 3 — Poll for completion **
curl https://walletapi.fluxapay.xyz/api/payouts/reward-task-42 \
  -H "Authorization: Bearer $AGENT_JWT"

Status lifecycle:
  authorized → signing → signed → broadcasting → succeeded → confirmed

** Step 4 — Confirm **
- If confirmed: "Payout of 2.00 USDC sent. Tx: {txHash}"
- If failed: explain failureReason and retry or surface to user

Flow B — without a mandate (user manual approval)

Use this for ad-hoc or high-value transfers where the user should review each payout individually.

text
** Your task **
Send 2.00 USDC to 0xABCD...1234 as a one-off withdrawal.

** Step 1 — Create the payout (no mandateId) **
curl -X POST https://walletapi.fluxapay.xyz/api/payouts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENT_JWT" \
  -d '{
    "payoutId": "withdraw-001",
    "toAddress": "0xABCD...1234",
    "amount": "2000000",
    "bizId": "withdraw_001",
    "description": "User withdrawal"
  }'

Expected response:
- status: "pending_authorization"
- approvalUrl: "https://agentwallet.fluxapay.xyz/authorize-payout/..."

** Step 2 — Ask the user to approve **
Surface the approvalUrl to the user. They open it in a browser and
approve (or deny) inside the FluxA Wallet. Status transitions:
  pending_authorization → authorized   (user approved)
  pending_authorization → user_denied  (user denied; terminal)
  pending_authorization → failed       (expired after ttlSeconds)

** Step 3 — Poll for completion **
curl https://walletapi.fluxapay.xyz/api/payouts/withdraw-001 \
  -H "Authorization: Bearer $AGENT_JWT"

Status lifecycle after approval:
  authorized → signing → signed → broadcasting → succeeded → confirmed

** Step 4 — Confirm **
- If confirmed: "Payout of 2.00 USDC sent. Tx: {txHash}"
- If user_denied / expired: explain and offer to retry

Choosing between the flows

  • Flow A (mandate) — agent has a user-signed budget covering this payout. No user interaction; payout starts broadcasting immediately.
  • Flow B (manual) — ad-hoc or high-value transfers. The user must approve each payout via approvalUrl before the worker signs.

See Mandates for how to propose, sign, and query mandates.

Using bizId for deduplication

When bizId is set, only one active payout can exist per user per bizId. This protects against accidental double-payment on retries. A bizId is released when its payout reaches a deterministic terminal failure (e.g. user_denied, signing_failed, tx_reverted), but not on tx_confirmation_timeout, which requires manual reconciliation.

Released under the MIT License.