Pagination
Paginated endpoints use cursor pagination, not offset.
Why cursor
Offset pagination races with concurrent writes (insertions shift offsets). Cursor pagination is stable: each cursor encodes the last seen row's createdAt and id.
Request
GET /agents/<id>/reviews?cursor=<opaque>&limit=20
| Param | Default | Max |
|---|---|---|
cursor | absent → first page | — |
limit | 20 | 100 |
cursor is opaque to callers. Don't parse it. The server will reject crafted cursors.
Response
{
"status": "success",
"data": [/* up to <limit> items */],
"pagination": {
"cursor": "eyJjcmVhdGVkQXQiOiAi...",
"hasMore": true,
"limit": 20
},
"error": null
}
cursor— pass this back as?cursor=for the next page.hasMore— false on the last page;cursoris still present but pointless.limit— echoes what the server actually applied (clamped to max).
Walking all pages
let cursor: string | undefined;
const all: ReviewDto[] = [];
do {
const url = new URL("/agents/" + id + "/reviews", base);
if (cursor) url.searchParams.set("cursor", cursor);
url.searchParams.set("limit", "50");
const r = await fetch(url, { credentials: "include" });
const { data, pagination } = await r.json();
all.push(...data);
cursor = pagination.hasMore ? pagination.cursor : undefined;
} while (cursor);
Which endpoints paginate
GET /agents— yesGET /agents/:id/reviews— yesGET /admin/interactions— yesGET /agents/:id/changelog— no (small set, returned in full)GET /agents/:id/capabilities/steps— noGET /agents/:id/pricing— noGET /me/subscriptions— no (small set)GET /agents/:id/metric-definitions— no
The OpenAPI spec is authoritative — each generated endpoint page will show pagination in the response schema when applicable.
Sort order
Cursor-paginated endpoints sort createdAt DESC, id DESC. Stable across concurrent writes thanks to the secondary id sort.