> ## Documentation Index
> Fetch the complete documentation index at: https://developers.bloobank.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> Cursor-based pagination via page_size, page_token, and nextPageToken — the reference contract for every list endpoint.

Every list endpoint in the BlooBank API follows the **same pagination contract**. Read this once and you know how to paginate any collection.

## Request parameters

| Parameter    | Type      | Description                                                                        |
| ------------ | --------- | ---------------------------------------------------------------------------------- |
| `filter`     | `string`  | Filter expression — see [Filtering (SFS-1)](/api-reference/conventions/filtering). |
| `order_by`   | `string`  | Result ordering — see [§Ordering](#order-by).                                      |
| `page_size`  | `integer` | Maximum items per page. Defaults to `100`; maximum is `100`.                       |
| `page_token` | `string`  | Opaque token returned as `nextPageToken` by a previous response.                   |

## Response shape

Every list response is shaped:

```json theme={"dark"}
{
  "items": [ /* page of resources */ ],
  "nextPageToken": "spct1.eyJ2IjoxLCJleHAiOjE3MzY1NTM2MDB9.aBcDeFgHiJkLmNoPqRsTuVwXyZ"
}
```

| Field           | Type           | Description                                                              |
| --------------- | -------------- | ------------------------------------------------------------------------ |
| `items`         | array          | The page of returned resources. Always present, even when empty.         |
| `nextPageToken` | string \| null | Opaque token for the next page. `null` when the result set is exhausted. |

## `page_size`

| Rule                         | Behavior                               |
| ---------------------------- | -------------------------------------- |
| Omitted                      | Server applies the default (`100`).    |
| Less than `1`                | `INVALID_PAGE_SIZE` (HTTP 400).        |
| Greater than maximum (`100`) | Coerced down to the maximum. No error. |
| Non-integer                  | `INVALID_PAGE_SIZE` (HTTP 400).        |

You **may** change `page_size` between pages without invalidating a token.

```http theme={"dark"}
GET /wallets?page_size=25
```

## `page_token`

`page_token` is a **pagination cursor** generated by the server. Clients must treat it as fully opaque.

### What it encodes (conceptually)

* The pagination protocol version
* Issuance and expiration timestamps
* A fingerprint of the original query context (`filter` and `order_by`)
* The cursor position corresponding to the last item of the previous page

The payload is serialized using MessagePack and cryptographically signed to prevent tampering.

### Guarantees

* **Bound to query context.** Using a token with a different `filter` or `order_by` (or against a different endpoint) returns `INVALID_PAGE_TOKEN`.
* **Bounded lifetime.** Tokens expire — typically within 48 hours. Expired tokens return `INVALID_PAGE_TOKEN`. Restart pagination from the beginning when this happens.
* **Tokens do not grant authorization.** Every paginated request is authorized independently.

### Rules

| Rule                                                    | Behavior                            |
| ------------------------------------------------------- | ----------------------------------- |
| `page_token` is optional.                               | Omit to request the first page.     |
| Must be a value previously returned as `nextPageToken`. | Synthesized tokens fail validation. |
| Opaque and URL-safe.                                    | Do not parse, modify, or construct. |
| Server validates context consistency.                   | See "Parameter stability" below.    |

### Parameter stability across pages

When using `page_token`, all request parameters **must match** the original request, with one exception.

| Parameter    |        Changeable across pages?       |
| ------------ | :-----------------------------------: |
| `filter`     |                   ❌                   |
| `order_by`   |                   ❌                   |
| `page_size`  |                   ✅                   |
| `page_token` | (changes per page — that's the point) |

Inconsistent requests fail with `INVALID_PAGE_TOKEN` (HTTP 400).

## `order_by`

The `order_by` parameter is a **comma-separated list of ordering expressions**.

### Syntax

* Ascending is the default: `field`
* Descending uses the ` desc` suffix: `field desc`
* Whitespace is ignored.
* Ordering applies left-to-right.

### Examples

```
order_by=createdAt desc
order_by=createdAt desc, status, name
order_by=updatedAt asc, createdAt desc
```

### Field restrictions

Ordering fields are **explicitly whitelisted by each endpoint**. The allowed fields are documented per endpoint (e.g., wallets list allows `createdAt`).

| Failure           | Response                      |
| ----------------- | ----------------------------- |
| Unknown field     | `INVALID_ORDER_BY` (HTTP 400) |
| Duplicate field   | `INVALID_ORDER_BY` (HTTP 400) |
| Invalid direction | `INVALID_ORDER_BY` (HTTP 400) |

### Stable tie-breaking

When multiple items share the ordering values you specified, the service appends an internal tie-breaker (typically the resource id) so pagination is deterministic. Clients **must not** rely on the presence, name, or direction of the tie-breaker.

## URL encoding

Query parameters travel through URLs and must follow RFC 3986.

* Spaces → `%20`
* `>` → `%3E`, `<` → `%3C`, `=` → `%3D`, `,` → `%2C`

```
filter=status%20%3D%20ACTIVE%20AND%20createdAt%20%3E%3D%202026-01-01
order_by=createdAt%20desc
```

Most HTTP clients encode automatically. Hand-built URLs must encode explicitly.

## End-of-collection

When no more results exist:

* `nextPageToken` is `null` (not omitted, not empty string — explicitly `null`).
* Clients should treat `nextPageToken === null` as end-of-collection.

## Errors

| Reason               | HTTP | When                                                         |
| -------------------- | ---: | ------------------------------------------------------------ |
| `INVALID_PAGE_SIZE`  |  400 | `page_size` is out of range or non-integer.                  |
| `INVALID_PAGE_TOKEN` |  400 | Token is malformed, expired, or context-mismatched.          |
| `INVALID_FILTER`     |  400 | `filter` violates SFS-1 syntax or the endpoint schema.       |
| `INVALID_ORDER_BY`   |  400 | `order_by` references an unknown field or invalid direction. |

See [Errors](/api-reference/conventions/errors) for the full envelope contract.

## End-to-end example

**Page 1** — request:

```http theme={"dark"}
GET /wallets/production-main/paymentOrders?filter=status=SUCCESS&order_by=createdAt%20desc&page_size=25
```

Response:

```json theme={"dark"}
{
  "items": [ /* 25 orders */ ],
  "nextPageToken": "spct1.eyJ2IjoxLCJleHAiOjE3MzY1NTM2MDB9.aBcDeFgHiJkLmNoPqRsTuVwXyZ"
}
```

**Page 2** — request (same `filter` and `order_by`, different `page_size`, with token):

```http theme={"dark"}
GET /wallets/production-main/paymentOrders?filter=status=SUCCESS&order_by=createdAt%20desc&page_size=50&page_token=spct1.eyJ2IjoxLCJleHAiOjE3MzY1NTM2MDB9.aBcDeFgHiJkLmNoPqRsTuVwXyZ
```

Response — end of collection:

```json theme={"dark"}
{
  "items": [ /* remaining orders */ ],
  "nextPageToken": null
}
```

## Client best practices

1. **Treat `page_token` as opaque** — do not parse, encode, or persist alongside business state.
2. **Keep `filter` and `order_by` stable** across pages of the same iteration.
3. **Tune `page_size` to the consumer** — small (10–25) for interactive UIs; large (up to 100) for batch jobs.
4. **Iterate until `nextPageToken` is `null`** — that is the only end-of-collection signal.
5. **Restart on `INVALID_PAGE_TOKEN`** — tokens expire; restart pagination from the beginning if the user resumes hours later.

## Next

<CardGroup cols={2}>
  <Card title="Pagination concepts" icon="arrow-down" href="/get-started/concepts/pagination">
    The mental model behind cursor pagination, when to paginate.
  </Card>

  <Card title="Ordering concepts" icon="arrow-down-wide-short" href="/get-started/concepts/ordering">
    How to choose stable ordering fields.
  </Card>
</CardGroup>
