Skip to content

Most payouts are paid from a USD balance you hold with Avvio. You top it up by wire or with USDC, and in the sandbox with one API call. Balance & funding explains what the balance is made of.

  1. Find where to wire with GET /payments/organizations/{orgId}/payin-accounts.
  2. In the sandbox, credit the balance with POST .../sandbox/fund instead.
  3. Check what you can send with GET /payments/organizations/{orgId}/balance.
  4. For a payout that returns requiresFunding: true, fund that one payout from your wallet.
  1. GET /payments/organizations/{orgId}/payin-accounts
    x-api-key: avvio_live_…
    {
    "accounts": [
    {
    "id": "sbx_acct_usd_cmsx0h2k900a1n11ib3qz9v42",
    "currency": "USD",
    "shape": "us",
    "status": "active",
    "paymentRails": ["wire", "ach"],
    "settlesTo": "provider_balance",
    "destination": null,
    "balance": { "currency": "USD", "amount": 980000, "decimals": 2 },
    "depositInstructions": {
    "bank_name": "Example Bank",
    "account_number": "****4471",
    "routing_number": "123456789",
    "beneficiary_name": "Avvio",
    "reference": "ORG-7f3a91"
    }
    }
    ]
    }

    Wire to depositInstructions. Money you wire is held as your balance; it is not forwarded anywhere, and payouts debit it. When there is nothing to show, a live key gets { "accounts": [] }; a test key also gets a note with the reason. USD stablecoins in your organization’s wallet also count toward the balance.

    Wire lead time depends on your bank and the receiving bank, so confirm it with your Avvio contact and fund ahead of your first live run.

  2. There is no wire in the sandbox, and no sandbox/fund in production:

    POST /payments/organizations/{orgId}/sandbox/fund
    IDEMPOTENCY_KEY=$(uuidgen) # new key per call; reuse it only to retry this exact request
    curl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/sandbox/fund" \
    -H "x-api-key: $AVVIO_API_KEY" \
    -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
    -H "content-type: application/json" \
    -d '{
    "amount": "5000.00"
    }'
    { "balance": "10000.00" }

    amount defaults to 10000.00. Skip this once to see the 400 INSUFFICIENT_BALANCE your integration gets when the balance is short.

  3. GET /payments/organizations/{orgId}/balance
    x-api-key: avvio_live_…
    {
    "currency": "USD",
    "amount": "13185.03",
    "balances": [{ "currency": "USD", "amount": "13185.03" }],
    "provider": [{ "currency": "USD", "amount": "10000.00" }],
    "wallet": [{ "currency": "USDC", "network": "Ethereum", "amount": "3185.030147" }],
    "unavailable": [],
    "ledger": [{ "currency": "USD", "available": "9800.00", "held": "200.00", "total": "10000.00" }]
    }

    Size payouts against balances. When unavailable is non-empty the figures are a floor, so retry before deciding you cannot fund a payout. Reading the balance explains every field.

  4. Some crypto and dedicated settlement routings price the transfer and wait for you to fund that one payout from your own wallet. You send the transfer from your registered wallet and report its transaction hash, unless your organization has opted in to automatic funding. A payout that needs funding says so in the creation response:

    {
    "requiresFunding": true
    }

    Call GET /payments/organizations/{orgId}/payouts/{payoutId}/funding:

    GET /payments/organizations/{orgId}/payouts/sbx_pay_7c1c0f2e-6d9b-4a3e-9d1a-2f4b8c9e0a11/funding
    x-api-key: avvio_live_…

    Response:

    {
    "payoutId": "sbx_pay_7c1c0f2e-6d9b-4a3e-9d1a-2f4b8c9e0a11",
    "amount": "200.00",
    "currency": "USDC",
    "depositAddress": "0x71C...492",
    "network": "base",
    "expiresAt": null
    }

    This is a pure read and starts no clock, so poll it freely.

    amount already includes the payout fee; nothing is added at funding time. See Fees.

    expiresAt is normally null; it carries a time only when the network publishes one. Before sending against old instructions, check the payout’s status at GET /payments/organizations/{orgId}/orders/{payoutId}.

    After you broadcast the transfer from your registered wallet, send its hash:

    POST /payments/organizations/{orgId}/payouts/sbx_pay_7c1c0f2e-6d9b-4a3e-9d1a-2f4b8c9e0a11/funding/confirm
    x-api-key: avvio_live_…
    Idempotency-Key: 5a81e921-bc01-447a-9a11-0982716a5b42
    Content-Type: application/json
    {
    "transactionHash": "0x98a123...45f"
    }

    transactionHash is required. Confirmation also accepts X-Allow-Duplicate: true (Idempotency).

    Before recording the deposit, Avvio checks the chain, on every network, Solana included. The transaction must:

    1. Exist on the designated network.
    2. Have successfully confirmed.
    3. Have transferred at least the required token amount to the exact depositAddress.
    4. Originate from the wallet registered for this payout, where the network exposes that check.
    Status Type Action
    400 FUNDING_TRANSACTION_INVALID We read the transaction and it failed a check (reverted, wrong address, too little). Nothing is recorded and the payout stays open; send a correct transfer and confirm its hash
    409 FUNDING_NOT_YET_VERIFIABLE We could not verify it yet: not mined or not indexed. Your transfer is unaffected. Back off and confirm the same hash again

    Errors lists the rest.

    POST /payments/organizations/{orgId}/payouts/{payoutId}/cancel
    IDEMPOTENCY_KEY=$(uuidgen) # new key per call; reuse it only to retry this exact request
    curl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/payouts/$payoutId/cancel" \
    -H "x-api-key: $AVVIO_API_KEY" \
    -H "Idempotency-Key: $IDEMPOTENCY_KEY"

    You can cancel only a payout still waiting on your own funding (requiresFunding). It reports canceled and can no longer be funded. It was never debited, so your balance does not change and no ledger row is written. Once funded, cancel returns PAYOUT_NOT_CANCELABLE; it cannot stop a payout in flight.

    Ask us to enable automatic funding and we fund these payouts from your organization’s own wallet. Your API calls stay the same.

    POST /payouts then sends the pay-in itself, a gas-covered USDC transfer from your wallet, verified as above. The response reads status: "processing", or pending with requiresFunding: true while it confirms, usually within a minute or two; poll the order or take the webhook. If the wallet can’t cover it or the transfer can’t be sent, nothing is sent and the payout is left as a manually funded one, requiresFunding: true with the instructions above still valid.

Was this page helpful?