Pagination
All collection endpoints use cursor-based pagination. There are no page numbers or offsets — they don't scale safely for ledger-shaped data where concurrent inserts can cause page drift.
Cursor-based
Every paginated endpoint returns a fixed envelope. Cursors are opaque base64url strings, HMAC-signed by the backend, with a 7-day TTL. Never parse or construct cursors manually — treat them as opaque tokens.
Response shape
interface PaginatedResponse<T> {
data: T[];
has_more: boolean;
next_cursor: string | null;
prev_cursor: string | null;
}| Field | Purpose |
|---|---|
data | Array of items on this page |
has_more | true if more items exist beyond this page |
next_cursor | Cursor for the next page (null if this is the last page) |
prev_cursor | Cursor for the previous page (null on the first page) |
Walking forward
Pass starting_after with the value of next_cursor to advance to the next page:
const baseUrl = process.env.YIELDFORCE_API_BASE_URL ?? 'https://yieldforce.io/api';
async function fetchAllTransactions(endUserId: string) {
const results = [];
let cursor: string | null = null;
do {
const url = new URL(
`/v1/partner/end_users/${endUserId}/transactions`,
baseUrl
);
url.searchParams.set('limit', '50');
if (cursor) url.searchParams.set('starting_after', cursor);
const res = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${entraToken}`,
'X-User-Token': partnerJwt,
},
});
const page = await res.json();
results.push(...page.data);
cursor = page.next_cursor;
} while (cursor !== null);
return results;
}Walking backward
Pass ending_before with the value of prev_cursor to go back one page:
const baseUrl = process.env.YIELDFORCE_API_BASE_URL ?? 'https://yieldforce.io/api';
const url = new URL('/v1/partner/end_users/alice-bunq-id/transactions', baseUrl);
url.searchParams.set('ending_before', prevCursor);
const res = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${entraToken}`,
'X-User-Token': partnerJwt,
},
});
const prevPage = await res.json();starting_after and ending_before are mutually exclusive. Providing both returns 400 with code: "conflicting_cursors".
Limit
The default page size is 25 items. The maximum is 100. Pass limit as a query parameter:
GET /v1/partner/end_users/alice-bunq-id/transactions?limit=100
The per-endpoint reference documents the effective default and maximum for each collection.
Cursor properties
- Opaque — do not parse, inspect, or construct cursor strings. The internal encoding may change without notice.
- HMAC-signed — the backend verifies the cursor signature on every use. A tampered cursor returns
400 cursor_invalid. - 7-day TTL — cursors expire 7 days after they are issued. An expired cursor returns
410 cursor_expired. Re-fetch the first page to get a fresh cursor. - Stable for the underlying ordering — each paginated endpoint has a documented stable sort order (typically
created_at DESC, id DESC). Re-issuing a request with the same cursor returns the same page, modulo rows added after the cursor was issued (new rows appear on subsequent pages).
Endpoints with pagination
- List transactions —
GET /v1/partner/end_users/{external_id}/transactions - List portfolios —
GET /v1/partner/end_users/{external_id}/portfolios