---
title: "Reserves"
description: "Read what part of your sales is held back, why, and when each amount is released."
url: https://docs.fluveo.com/payments/reserves/
---

> 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/reserves.curl.md (cURL), https://docs.fluveo.com/payments/reserves.node.md (Node.js), https://docs.fluveo.com/payments/reserves.python.md (Python), https://docs.fluveo.com/payments/reserves.php.md (PHP).

# Reserves

Read what part of your sales is held back, why, and when each amount is released.

A reserve keeps part of your sales back for a while, so refunds and disputes stay covered. Fluveo sets the reserve and its terms on your store's account. While it is in place, part of each sale waits in it instead of joining what you can pay out. Read this page to know how much is held, under which terms, and when it comes back.

The reserve routes are a Fluveo extension: the reference lists them under Fluveo extensions, not as part of API version `2026-05-27.dahlia`, and they serve the same routes, lists and fields to every store. Every one of them is a read. See [Reads only](https://docs.fluveo.com/payments/reserves.md#reads-only).

## Plans, holds and releases

Three objects describe a reserve. Each list answers with your store's objects only, and the holds and releases come newest first.

| Object | Id | What it is |
| --- | --- | --- |
| `reserve.plan` | `resplan_...` | The terms: the share of each sale to keep back (`percent`), the way it is released (`type`, such as `rolling_release`), and its `status`. |
| `reserve.hold` | `reshold_...` | One amount kept back. A hold taken from a sale can name its plan in `reserve_plan` and the sale's charge in `source_charge`; `reason` is `charge`. A hold made on its own has `reason` `standalone`. |
| `reserve.release` | `resrel_...` | Money that left a hold and is yours again. It can name the hold in `reserve_hold` and its plan in `reserve_plan`. |

A plan has many holds, and a hold can have one release or more. A store whose reserve follows no plan has an empty plans list.

## List your reserve plans

`GET /v1/reserve/plans` lists the plans of your store's account. Narrow it with `status`: `active`, `disabled` or `expired`.

**List the active reserve plans**

cURL:

```bash
curl -G https://api.fluveo.com/v1/reserve/plans \
  -H "Authorization: Bearer sk_test_..." \
  -d status=active
```

Node.js:

```javascript
const query = new URLSearchParams({ status: "active" });
const response = await fetch("https://api.fluveo.com/v1/reserve/plans?" + query, {
  headers: { Authorization: "Bearer sk_test_..." },
});
const plans = await response.json();
console.log(plans.data.length);
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/reserve/plans",
    headers={"Authorization": "Bearer sk_test_..."},
    params={"status": "active"},
)
plans = response.json()
print(len(plans["data"]))
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/reserve/plans?" . http_build_query(["status" => "active"]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
]);
$plans = json_decode(curl_exec($ch), true);
echo count($plans["data"]);
```

A store with no reserve gets an empty list:

*Response*

```json
{
  "object": "list",
  "url": "/v1/reserve/plans",
  "has_more": false,
  "data": []
}
```

When a plan is in place, each row reads like this one, abridged. It keeps 5% of each sale and releases each amount 90 days after its charge:

*A reserve.plan, abridged*

```json
{
  "id": "resplan_...",
  "object": "reserve.plan",
  "created_by": "fluveo",
  "currency": null,
  "percent": 5,
  "status": "active",
  "type": "rolling_release",
  "rolling_release": {
    "days_after_charge": 90
  }
}
```

`currency` is null when the plan covers every currency. `created_by` says who made the plan, `fluveo` or `application`.

## List the held amounts

`GET /v1/reserve/holds` lists every amount kept back. Narrow it with `reserve_plan` (a `resplan_...`), `source_charge` (a `ch_...` of one of your sales), `reserve_release` (the holds one release took money from), `currency`, and `is_releasable` (`true` or `false`).

**List the held amounts**

cURL:

```bash
curl -G https://api.fluveo.com/v1/reserve/holds \
  -H "Authorization: Bearer sk_test_..." \
  -d limit=10
```

Node.js:

```javascript
const query = new URLSearchParams({ limit: "10" });
const response = await fetch("https://api.fluveo.com/v1/reserve/holds?" + query, {
  headers: { Authorization: "Bearer sk_test_..." },
});
const holds = await response.json();
console.log(holds.data.length, holds.has_more);
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/reserve/holds",
    headers={"Authorization": "Bearer sk_test_..."},
    params={"limit": 10},
)
holds = response.json()
print(len(holds["data"]), holds["has_more"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/reserve/holds?" . http_build_query(["limit" => 10]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
]);
$holds = json_decode(curl_exec($ch), true);
echo count($holds["data"]);
```

*Response*

```json
{
  "object": "list",
  "url": "/v1/reserve/holds",
  "has_more": false,
  "data": []
}
```

A hold taken from a sale looks like this, abridged. `amount` and `amount_releasable` are in the smallest unit of the currency, so `250` is 2.50:

*A reserve.hold, abridged*

```json
{
  "id": "reshold_...",
  "object": "reserve.hold",
  "amount": 250,
  "amount_releasable": 0,
  "currency": "usd",
  "created_by": "fluveo",
  "destination": "risk_reserved",
  "is_releasable": false,
  "reason": "charge",
  "release_schedule": {
    "release_after": 1786060800,
    "scheduled_release": 1786060800
  },
  "reserve_plan": "resplan_...",
  "source_charge": "ch_...",
  "source_type": "card"
}
```

`release_schedule` holds the date the hold is due back. `amount_releasable` is the part that may be released now, and `is_releasable` is `true` when the hold may be released.

## List the releases

`GET /v1/reserve/releases` lists the money that left your holds. Narrow it with `reserve_hold` (a `reshold_...`), `reserve_plan` (a `resplan_...`) and `currency`.

**List the releases**

cURL:

```bash
curl -G https://api.fluveo.com/v1/reserve/releases \
  -H "Authorization: Bearer sk_test_..." \
  -d limit=10
```

Node.js:

```javascript
const query = new URLSearchParams({ limit: "10" });
const response = await fetch("https://api.fluveo.com/v1/reserve/releases?" + query, {
  headers: { Authorization: "Bearer sk_test_..." },
});
const releases = await response.json();
console.log(releases.data.length, releases.has_more);
```

Python:

```python
import requests

response = requests.get(
    "https://api.fluveo.com/v1/reserve/releases",
    headers={"Authorization": "Bearer sk_test_..."},
    params={"limit": 10},
)
releases = response.json()
print(len(releases["data"]), releases["has_more"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/reserve/releases?" . http_build_query(["limit" => 10]));
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer sk_test_..."],
]);
$releases = json_decode(curl_exec($ch), true);
echo count($releases["data"]);
```

*Response*

```json
{
  "object": "list",
  "url": "/v1/reserve/releases",
  "has_more": false,
  "data": []
}
```

A release can name its hold, its amount in the smallest currency unit, and `released_at`, the time the money became available again. `destination` is the balance it went back to, such as `payments`. `reason` tells why it left the hold: on its date, early, by a refund, or with its plan. When a refund caused it, `source_transaction` says which.

## Read one by id

Each object has its own read, which answers exactly as its row in the list:

- `GET /v1/reserve/plans/{id}` reads one `resplan_...`.
- `GET /v1/reserve/holds/{id}` reads one `reshold_...`. Add `expand[]=reserve_plan` or `expand[]=source_charge` to get the whole object in place of its id.
- `GET /v1/reserve/releases/{id}` reads one `resrel_...`. Add `expand[]=reserve_plan` or `expand[]=reserve_hold` the same way.

Take the id from a list or from a `reserve.*` event. An id that is not one of your store's answers 404. See [Expanding responses](https://docs.fluveo.com/start/expand.md).

**List parameters**

- `status` (string, optional): Plans only. `active`, `disabled` or `expired`.
- `reserve_plan` (string, optional): Holds and releases. Only those of this `resplan_...`.
- `source_charge` (string, optional): Holds only. Only the holds taken from this charge, a `ch_...`.
- `reserve_release` (string, optional): Holds only. Only the holds this `resrel_...` released money from.
- `is_releasable` (string, optional): Holds only. `true` for holds that may be released now, `false` for the others.
- `reserve_hold` (string, optional): Releases only. Only the releases of this `reshold_...`.
- `currency` (string, optional): Holds and releases. Only those in this three-letter currency code.
- `limit` (integer, optional): How many objects one page holds, from 1 to 100. Default 10.
- `starting_after` (string, optional): The id of the last object of the page you have, to read the next page. See [Pagination](https://docs.fluveo.com/start/pagination.md).
- `ending_before` (string, optional): The id of the first object of the page you have, to read the page before it. Never send it with `starting_after`.

## The held money in your balance

`GET /v1/balance` shows what your reserve holds in `risk_reserved`, with its own `available` and `pending` lists, one entry per currency in the smallest currency unit. That money is already left out of the balance's top-level `available` and `pending`, so it is never counted twice, and you cannot pay it out until it is released. See [Read your balance](https://docs.fluveo.com/payments/payouts.md#read-your-balance) and [Balance and payouts](https://docs.fluveo.com/payments/payouts.md).

## When a hold is released

A hold is released on its release date, the one in its `release_schedule`. Fluveo may also release it earlier. A refund can trigger a release too. A hold made on its own, with no sale, has no date: it stays until Fluveo releases it. Each release shows as a `reserve.release` object, and the hold's `amount_releasable` and `is_releasable` change with it.

When a plan is turned off or reaches its end, its holds are released.

## Reads only

The reserve routes only read. Your store does not create a reserve plan or a hold, change one, or release money from a hold through the API, and no route takes a POST for them. Fluveo sets the reserve and its terms.

## Plan statuses and events

| Plan status | Meaning |
| --- | --- |
| `active` | The plan keeps part of each new sale. |
| `disabled` | The plan was turned off. Its holds are being released. |
| `expired` | The plan reached its end. Its holds are being released. |

A newer value can show as sent: treat any other value as unknown, not as an error.

| Event | When |
| --- | --- |
| [`reserve.plan.created`](https://docs.fluveo.com/webhooks/events.md#reserve.plan.created) | A plan was set up on your store's account. |
| [`reserve.plan.updated`](https://docs.fluveo.com/webhooks/events.md#reserve.plan.updated) | A plan's terms changed. |
| [`reserve.plan.disabled`](https://docs.fluveo.com/webhooks/events.md#reserve.plan.disabled) | A plan was turned off. |
| [`reserve.plan.expired`](https://docs.fluveo.com/webhooks/events.md#reserve.plan.expired) | A plan reached its end. |
| [`reserve.hold.created`](https://docs.fluveo.com/webhooks/events.md#reserve.hold.created) | Money from a sale was held back. |
| [`reserve.hold.updated`](https://docs.fluveo.com/webhooks/events.md#reserve.hold.updated) | A hold changed, such as its release date or the part still held. |
| [`reserve.release.created`](https://docs.fluveo.com/webhooks/events.md#reserve.release.created) | Money left a hold: on its date, early, or by a refund. |

Each event's `data.object` is the plan, hold or release as it was then. See [Webhooks](https://docs.fluveo.com/webhooks.md).

## Errors you may see

| Error | Why |
| --- | --- |
| 404 [`resource_missing`](https://docs.fluveo.com/errors.md#resource_missing) | An unknown reserve plan, hold or release id in a path, filter or cursor is not one of your store's. The message names the object, such as "Specified Reserve Hold not found." An unknown `source_charge` filter returns an empty list. |
| 400 [`parameter_unknown`](https://docs.fluveo.com/errors.md#parameter_unknown) | The list does not take that parameter, such as `status` on the holds list. |
| 400 [`parameter_invalid_integer`](https://docs.fluveo.com/errors.md#parameter_invalid_integer) | `limit` is not a whole number. A whole number outside 1 to 100 is brought into that range instead of refused. |
| 400 "Invalid status: must be one of active, disabled, or expired" | The plans list got a `status` outside those three. This error has [no code](https://docs.fluveo.com/errors.md#no-code). |
| 400 "Received both starting_after and ending_before parameters. Please pass in only one." | Send one cursor at a time. This error has [no code](https://docs.fluveo.com/errors.md#no-code). |
| 403 | A restricted key without Read on Reserves. See [Restricted keys](https://docs.fluveo.com/start/authentication.md#restricted-keys). |

A refusal looks like this:

*404 response*

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_missing",
    "message": "Specified Reserve Hold not found."
  }
}
```

## Test it

1. With a test key, list the plans, the holds and the releases as above. A new test store with no reserve gets `"object": "list"` and an empty `data` from each of the three. If Fluveo set a plan on your store, the plans list shows it with `created_by: "fluveo"`.
2. Read `GET /v1/balance` and find `risk_reserved` (see [Read your balance](https://docs.fluveo.com/payments/payouts.md#read-your-balance)).
3. Read `GET /v1/reserve/holds/{id}` with an id your store does not have, such as `reshold_000000000000000000000000`: 404 `resource_missing`.
4. Send `status=held` to the plans list: 400 "Invalid status: must be one of active, disabled, or expired".

> **For AI agents:**
>
> Reserves are read-only: GET /v1/reserve/plans, /v1/reserve/holds and /v1/reserve/releases, and each by id. Never try to create, change or release a reserve through the API; no such route exists. An empty list is a normal answer for a store with no reserve, not an error. Amounts are in the smallest currency unit. Held money is in GET /v1/balance risk_reserved and is not in the top-level available or pending; do not add it back and do not count it as payable. Filters: plans take status (active, disabled, expired); holds take reserve_plan, source_charge, reserve_release, currency, is_releasable; releases take reserve_hold, reserve_plan, currency. Treat an unknown status or reason value as unknown, not as an error. Follow reserve.hold.created and reserve.release.created to track held and released money.

## Next

- [Balance and payouts](https://docs.fluveo.com/payments/payouts.md): Read your balance and send money to your bank.
- [Refund a payment](https://docs.fluveo.com/payments/refunds.md): Give a buyer money back from your balance.
- [Handle disputes](https://docs.fluveo.com/payments/disputes.md): Answer a dispute and follow it to the end.
- [Event types](https://docs.fluveo.com/webhooks/events.md): Every event Fluveo sends, reserve events included.
- [The Reserves reference](https://docs.fluveo.com/api/reserves.md): Every field of the plan, hold and release objects.
