---
title: "Balance and payouts"
description: "Read your balance, see your statement, and pay out to your bank."
url: https://docs.fluveo.com/payments/payouts/
---

> Index of every page: https://docs.fluveo.com/llms.txt. Integrating Fluveo? Read https://docs.fluveo.com/agents/integrate.md first.

> Too long? This page with code in one language only: https://docs.fluveo.com/payments/payouts.curl.md (cURL), https://docs.fluveo.com/payments/payouts.node.md (Node.js), https://docs.fluveo.com/payments/payouts.python.md (Python), https://docs.fluveo.com/payments/payouts.php.md (PHP).

# Balance and payouts

Read your balance, see your statement, and pay out to your bank.

Every sale you take adds to your store's balance at Fluveo, kept in your store's currency. A new sale sits in **pending** for a while. Once it settles it moves to **available**, and only available money can leave for your bank. This page reads the balance, sends a payout and follows it until the money lands.

## Read your balance

`GET /v1/balance` returns two lists, each with one entry per currency, in the smallest unit of the currency:

- `available`: what you can send to your bank right now.
- `pending`: sales that have not settled yet.

`available` can go below zero. It is what your account holds less any amount your store owes Fluveo. A dispute takes its amount and its fee as soon as it opens, and a refund the balance did not cover counts while it waits. A refund or a dispute can take out more than the balance held, and the balance then shows the real, negative number, never zero. While `available` is below a payout's amount, the payout is refused with `balance_insufficient`, or with `payouts_not_allowed` when the account cannot pay out now for another reason.

**Read your balance**

cURL:

```bash
curl https://api.fluveo.com/v1/balance \
  -H "Authorization: Bearer sk_test_..."
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/balance", {
  headers: { Authorization: "Bearer sk_test_..." },
});
const balance = await response.json();
console.log(balance.available, balance.pending);
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/balance",
    headers={"Authorization": "Bearer sk_test_..."},
)
balance = response.json()
print(balance["available"], balance["pending"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/balance");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
]);
$balance = json_decode(curl_exec($ch), true);
print_r($balance["available"]);
```

The answer, trimmed:

*Response (trimmed)*

```json
{
  "object": "balance",
  "available": [{ "amount": 12500, "currency": "usd" }],
  "pending": [{ "amount": 4000, "currency": "usd" }],
  "livemode": false
}
```

When pending money becomes available, Fluveo sends one `balance.available` event, whose `data.object` is your balance as `GET /v1/balance` shows it at that moment. Listen for it if you pay out automatically.

Refunds and disputes also draw on your balance. See [Refund a payment](https://docs.fluveo.com/payments/refunds.md#balance).

## What a payout may take

Available money is not always free to leave. A payout already on its way, a hold on your account, or an amount your store still owes lowers what a new payout may take. So does a refund of a sale whose own money has not arrived yet, until that money arrives. A payout that asks for more is refused, and the error message names the reason (see [Errors you may see](https://docs.fluveo.com/payments/payouts.md#errors)).

When your store has a reserve, the part of each sale it holds stays out of `available` until it is released. [Reserves](https://docs.fluveo.com/payments/reserves.md#balance) shows how much is held and when it comes back.

## Create a payout

### Step 1: Check your bank account

Payouts go to the bank account your store registered for payouts. A store without one cannot pay out.

Every payout names the bank account it pays in `destination`, as a `ba_...` id. You may leave `destination` out of the request to pay your payout bank account, or send the id of one of your store's bank accounts to pay that one. Any other id is refused, so money never goes to an account your store did not register.

`GET /v1/accounts/{account}/external_accounts` lists your store's bank accounts, and a payout read with `expand[]=destination` shows the one it pays. Each shows the last four digits of the account number in `last4` and the bank's name in `bank_name`, or `null` when Fluveo has no name for it. The test bank of routing number `110000000` is named `FLUVEO TEST BANK`.

### Step 2: Send the payout

Give the amount in the currency's smallest unit, plus your store's currency. Add an `Idempotency-Key` made for this one payout. If the call is sent again with the same key and body, you get the first `po_` back and nothing is paid a second time.

**Pay out $95.00**

cURL:

```bash
curl https://api.fluveo.com/v1/payouts \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: payout-2026-11-07" \
  -d amount=9500 \
  -d currency=usd \
  -d "description=Weekly payout"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/payouts", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk_test_...",
    "Idempotency-Key": "payout-2026-11-07",
  },
  body: new URLSearchParams({ amount: "9500", currency: "usd", description: "Weekly payout" }),
});
const payout = await response.json();
console.log(payout.id, payout.status);
```

Python:

```python
import requests

response = requests.post(
    "https://api.fluveo.com/v1/payouts",
    headers={
        "Authorization": "Bearer sk_test_...",
        "Idempotency-Key": "payout-2026-11-07",
    },
    data={"amount": 9500, "currency": "usd", "description": "Weekly payout"},
)
payout = response.json()
print(payout["id"], payout["status"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/payouts");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: payout-2026-11-07",
    ],
    CURLOPT_POSTFIELDS => http_build_query([
        "amount" => 9500,
        "currency" => "usd",
        "description" => "Weekly payout",
    ]),
]);
$payout = json_decode(curl_exec($ch), true);
echo $payout["id"] . " " . $payout["status"];
```

### Step 3: Follow it to the end

A new payout is `pending`. Fluveo first sets the whole amount aside in your balance, and only then sends it. While the bank is receiving it, it can read `in_transit`. `paid` means your bank has it. A bank that refuses it turns it `failed`, and the amount returns to your balance. A payout can also turn `failed` after `paid`, when the bank sends the money back later; that amount returns to your balance too.

**Parameters**

- `amount` (integer, required): In the smallest unit of the currency, at least 1.
- `currency` (string, required): Your store's currency, as a three-letter code in lowercase. Any other currency is refused.
- `description` (string, optional): Your own text, up to 5,000 characters.
- `statement_descriptor` (string, optional): Text that may show on your bank statement. It cannot hold `<`, `>`, `'` or `"`.
- `method` (string, optional): Only `standard`, the default. `instant` is refused with 400 `instant_payouts_unsupported`.
- `destination` (string, optional): The id (`ba_...`) of one of your store's bank accounts, as `GET /v1/accounts/{account}/external_accounts` lists them. Leave it out to use your payout bank account for the currency.
- `metadata` (object, optional): Your own key-value pairs, sent as `metadata[key]=value`.

## Payout statuses

| Status | Meaning |
| --- | --- |
| `pending` | Accepted and not sent yet. You can still cancel it while none of it has left. |
| `in_transit` | Sent; your bank has not confirmed it yet. |
| `paid` | Your bank has the money. |
| `failed` | The bank refused it, or returned it after it was paid. The amount is back in your balance, and `failure_code` and `failure_message` explain. |
| `canceled` | You canceled it before it was sent. The amount is back in your balance. |

A payout keeps the `destination` it was created with. If your store removes or replaces that bank account before the money leaves, the payout fails with `failure_code` `account_closed` (`no_account` when no account is left on file), and the amount returns to your balance. Fluveo never redirects it to the new account.

## Events

| Event | When |
| --- | --- |
| `payout.created` | The payout was created. |
| `payout.updated` | The payout's status changed (sent beside each of the events below), the bank trace it shows (`trace_id`) changed, or you changed its metadata. |
| `payout.paid` | All of its money reached your bank. |
| `payout.failed` | The payout failed, or a paid payout had a bank return. The money is back in your balance. |
| `payout.canceled` | The payout was canceled before it was sent. |

## Payout schedule

Your store starts on `manual`: money leaves only when you create a payout. To be paid out on a schedule instead, set it with `POST /v1/balance_settings`, and read it back with `GET /v1/balance_settings`.

*Pay out every Monday*

```bash
curl https://api.fluveo.com/v1/balance_settings \
  -H "Authorization: Bearer sk_test_..." \
  -d "payments[payouts][schedule][interval]=weekly" \
  -d "payments[payouts][schedule][weekly_payout_days][0]=monday"
```

- `payments[payouts][schedule][interval]` is `manual` (the default), `daily`, `weekly` or `monthly`. `daily` pays out each weekday, Monday to Friday.
- With `weekly`, send the days in `payments[payouts][schedule][weekly_payout_days][]`, from `monday` to `friday`.
- With `monthly`, send the days of the month in `payments[payouts][schedule][monthly_payout_days][]`, from `1` to `31`. A day the month lacks pays on its last day.

On each scheduled day, Fluveo creates a payout of what your store may pay out then (see [What a payout may take](https://docs.fluveo.com/payments/payouts.md#payable)). It is an ordinary `po_` with `automatic: true`, and it sends the same events as any payout. You can still create a manual payout beside the schedule. Set `interval=manual` to stop the schedule.

## Cancel, read and list payouts

- `POST /v1/payouts/{id}/cancel` stops a payout that is still `pending`, before any of it was sent. After that the cancel is refused, so treat a payout as final once you create it.
- `POST /v1/payouts/{id}` changes only `metadata`.
- `GET /v1/payouts/{id}` reads one payout.
- `GET /v1/payouts` lists payouts, newest first. Filter with `status`, `created`, `arrival_date` or `destination`, and page with `limit` (1 to 100), `starting_after` or `ending_before`.

*Cancel a payout*

```bash
curl -X POST https://api.fluveo.com/v1/payouts/po_.../cancel \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: payout-2026-11-07-cancel"
```

## Your statement: balance transactions

Each charge, refund and payout leaves a balance transaction (`txn_`) with its `amount`, `fee`, `net`, `type` and `source` (the object that caused it).

*The statement lines of one payment*

```bash
curl -G https://api.fluveo.com/v1/balance_transactions \
  -H "Authorization: Bearer sk_test_..." \
  -d source=ch_... \
  -d limit=20
```

- `GET /v1/balance_transactions` lists lines, newest first. Filter with `type`, `source`, `currency` or `created`; page with `limit`, `starting_after` or `ending_before` (see [Pagination](https://docs.fluveo.com/start/pagination.md)). Add `expand[]=data.source` to get each source object in the same answer.
- `GET /v1/balance_transactions/{id}` reads one line.
- `payout=po_...` with an automatic payout (one your [payout schedule](https://docs.fluveo.com/payments/payouts.md#payout-schedule) made, `automatic: true`) lists the lines that payout paid out, its own `payout` line included. Another payout's `payout` line, manual or automatic, is never in it: that payout paid its money out. The `reserved_funds` line that moved part of a sale into your reserve is in the list of the payout it made smaller; the reserve's own side (`reserve_hold` and `reserve_release`, balance `risk_reserved`) never is. Other payouts, open disputes or debts can keep the listed lines' `net` from adding to 0. A manual payout is refused with 400 on `payout`: "Balance transaction history can only be filtered on automatic transfers, not manual." A value that is not one of your store's payouts answers 400 `resource_missing` on `payout`. To find the line any payout left, filter with `source=po_...`.

## Errors you may see

| Error | Why |
| --- | --- |
| 400 `balance_insufficient` | The payout asks for more than you can pay out now, as when what your store owes takes the available balance below it. Read `GET /v1/balance` and ask for less. |
| 400 `payouts_not_allowed` | Your account cannot pay out right now: payouts are not enabled yet, identity verification is not approved, there is a hold or an amount owed, or an earlier payout is not settled. The message says which. |
| 400 "Sorry, you don't have any external accounts in that currency" | No payout bank account is on file for the store. |
| 400 `amount_too_large` on `amount` | The amount is above your account's limit for one payout. |
| 400 `parameter_invalid_integer` on `amount` | The amount is above 9999999999, the most any payout can be. |
| 400 `instant_payouts_unsupported` | You sent `method=instant`. |
| 400 on `currency` | The currency is not your store's. |
| 400 `resource_missing` on `destination` | The destination is not one of your store's bank accounts. It is checked after the form of the request (an unknown parameter, a missing or invalid `amount` or `currency`, `metadata`, a `description` over 5,000 characters), and before `method`, a `statement_descriptor` with a forbidden character, the amount's maximum, a currency that is not your store's and your balance. |

A refused payout reserves and sends nothing.

See [Errors](https://docs.fluveo.com/errors.md) for the shape of every error.

## Test it

1. Take a few test payments with `pm_card_visa` (see [Accept a payment](https://docs.fluveo.com/payments/accept-a-payment.md)).
2. `GET /v1/balance`: the payments show in `pending` or `available`.
3. If `available` is more than `0`, create a small payout, such as `amount=100`: you get a `po_` in `pending` and `payout.created`. If it is refused instead, the message names the reason.
4. Ask for more than your available balance: `400 balance_insufficient` (or `payouts_not_allowed`), and no payout is made.
5. Send `method=instant`: `400 instant_payouts_unsupported`.
6. Send step 3 again with the same `Idempotency-Key`: you get the same `po_` back.

A test payout needs available test money. A test Pix or Boleto paid with the payer email `succeed_immediately@example.com` adds it once its money is available. See [Pix](https://docs.fluveo.com/payments/pix.md#test-it).

### Test a failed payout

In test mode, set your payout bank account (in the dashboard's payout settings) to one of these test bank accounts, with the routing number `110000000`. The next payout goes to it and fails with that code: you get `payout.failed`, the payout's `failure_code`, and the amount back in your balance.

| Test account number | `failure_code` |
| --- | --- |
| `000111111116` | `no_account` |
| `000111111113` | `account_closed` |

After a failure the bank account shows as errored, and payouts stay off until you set a working bank account, such as `000123456789`. These numbers work only in test mode.

> **For AI agents:**
>
> Balance and payouts for agents: read GET /v1/balance before POST /v1/payouts and never ask for more than the available amount; on 400 balance_insufficient or payouts_not_allowed, report the message's reason to the user instead of retrying. POST /v1/payouts takes amount (smallest unit), currency (the store's), optional description, statement_descriptor and metadata, and an Idempotency-Key unique to that payout; reuse it on retry, because a new key can create a second payout. Never send method=instant. Send destination only as the ba_ id of one of this store's bank accounts, or leave it out. Treat a payout as done only when its status is paid. Filter balance transactions with payout=po_... only for an automatic payout (automatic: true); a manual po_ is refused with 400 on payout, so use source=po_... to find a payout's own line. POST /v1/balance_settings with payments[payouts][schedule][interval] in {manual, daily, weekly, monthly} sets the payout schedule; manual is the default, and a manual payout stays allowed beside a schedule.

## Next

- [Payments](https://docs.fluveo.com/payments.md)
- [Event types](https://docs.fluveo.com/webhooks/events.md)
- [The Payouts reference](https://docs.fluveo.com/api/payouts.md) and [the Balance reference](https://docs.fluveo.com/api/balance.md).
