Skip to main content

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.

Every list endpoint in the BlooBank API uses cursor-based pagination with opaque tokens. This page gives the mental model; for the exact request/response contract, see the Pagination reference.

The mental model

Think of pagination as a cursor over a stable result set:
  1. Your first request defines the cursor by supplying filter, order_by, and page_size.
  2. The server returns a page of items plus a nextPageToken — a sealed envelope describing where the cursor stopped.
  3. On the next call, you hand that envelope back as page_token. The server resumes the cursor.
  4. When the server runs out of items, nextPageToken is null. You stop.
The token is opaque to you. Do not parse it, log it, or persist it next to business state. It carries:
  • The pagination protocol version
  • An expiration timestamp (typically 48 hours)
  • A fingerprint of filter + order_by (so you cannot mix-and-match across iterations)
  • The cursor position
This is the same model used by Stripe, GCP, and the AWS SDK list operations — chosen over offset-based pagination because it is robust to inserts and deletes between pages.

Why not offset-based?

Offset-based pagination (?offset=100&limit=20) is intuitive but breaks at scale:
ProblemEffect
Drift on insertA new row inserted between page 1 and page 2 shifts every subsequent page, causing items to appear twice.
Drift on deleteA row deleted between pages causes items to be skipped.
Cost grows with offsetThe database must scan offset rows to skip them — fast for the first page, slow for page 1000.
Cursor-based pagination avoids all three. Pages are anchored to specific items, not positions.

When to paginate

Use casepage_size recommendation
Interactive UI (table view)10–25
Background reconciliation job50–100
Full-collection export100 (the maximum)
The server’s maximum is 100. Exceeding it does not error — the value is silently coerced down.

The shape of a list response

Every list endpoint returns this envelope:
{
  "items": [ /* ... */ ],
  "nextPageToken": "spct1.eyJ2IjoxLCJleHAiOjE3MzY1NTM2MDB9.aBcDeFgHiJkLmNoPqRsTuVwXyZ"
}
When the collection is exhausted, nextPageToken is explicitly null (not omitted, not empty string). That is your single signal for end-of-iteration.

The “iterate until null” pattern

async function* iterateAllOrders(wallet: string, filter?: string) {
  let pageToken: string | null = null;
  do {
    const params = new URLSearchParams();
    if (filter) params.set('filter', filter);
    if (pageToken) params.set('page_token', pageToken);
    params.set('page_size', '100');

    const resp = await api.get(`/wallets/${wallet}/paymentOrders?${params}`);
    for (const item of resp.items) yield item;
    pageToken = resp.nextPageToken;
  } while (pageToken !== null);
}

for await (const order of iterateAllOrders('production-main', 'status=SUCCESS')) {
  // process order
}
The pattern: iterate, yield, advance, stop when token is null.

What you must keep stable across pages

Across calls within a single iteration, you must keep these identical:
  • filter
  • order_by
  • The endpoint URL
Changing any of them with a token from a previous query returns INVALID_PAGE_TOKEN (HTTP 400). The token is bound to the query context. You may change:
  • page_size (the server honors the new value)

When tokens expire

Tokens live ~48 hours. If a user pauses an iteration and resumes a day later, you might still be within the window. If they resume a week later, the token will return INVALID_PAGE_TOKEN. Handle this gracefully:
try {
  const resp = await api.get(`/wallets/${wallet}/paymentOrders?page_token=${pageToken}`);
} catch (err) {
  if (err.status === 400 && err.reason === 'INVALID_PAGE_TOKEN') {
    // Restart pagination from the beginning
    pageToken = null;
    continue;
  }
  throw err;
}
Restarting is acceptable — your filter and order_by are unchanged, so you will re-traverse from the start. For idempotent consumers (using idempotencyKey or upserts), re-traversal is harmless.

Reference

Pagination — full reference contract

Request parameters, response shape, errors, URL encoding.