Pagination and filtering
Traverse opaque keyset pages, poll transcripts forward, and apply each collection's supported filters.
Long lists come back one page at a time. Pass the cursor from one page into the next request to keep reading. Transcripts add a second cursor so you can poll for new messages without re-reading old ones.
Contract
A standard page is { data, nextCursor }. Pass a non-null nextCursor back as
cursor to the same endpoint with the same filters. null means there are no
more pages in that direction. Cursors are opaque: do not decode, edit, or reuse
one on another list. A cursor the API cannot read returns 400 invalid_cursor; other bad paging parameters return 400 validation_failed.
Lists run newest first: agent versions by version number, tasks by
updatedAt, memories by createdAt then id, and other lists by their own
timestamp. Transcripts return the newest page first, with messages in
chronological order inside each page.
| Collection | SDK method | REST operation | Response cursor fields | Page size | Filters |
|---|---|---|---|---|---|
| Agents | agents.list | list-agents | none | bounded; no public limit | userId |
| Agent Versions | agents.listVersions | list-agent-versions | nextCursor | default 50, max 200 | none |
| Providers | providers.list | list-providers | none | bounded; no public limit | none |
| MCP Connections | mcpConnections.list | list-mcp-connections | none | bounded; no public limit | none |
| MCP Attachments | agents.listMcpAttachments | list-agent-mcp-attachments | none | bounded; no public limit | owning Agent path |
| Prompts | prompts.list | list-prompts | none | bounded; no public limit | userId |
| Sessions | sessions.list | list-sessions | nextCursor | default 50, max 200 | userId |
| Latest Sessions | sessions.listLatest | list-latest-sessions | nextCursor | default 50, max 200 | userId, byAgent |
| Session messages | sessions.messages | list-session-messages | nextCursor, latestCursor | default 50, max 200 | cursor or after |
| Artifacts | artifacts.list | list-artifacts | nextCursor | fixed 50; no public limit | agentId, sessionId |
| Memories | memories.list | list-memories | nextCursor | default 50, max 100 | userId, search |
| Tasks | tasks.list | list-tasks | nextCursor | default 50, max 200 | agentId, userId |
| Task runs | tasks.listRuns | list-task-runs | nextCursor | default 50, max 200 | owning Task path |
| Task run messages | tasks.runMessages | list-task-run-messages | nextCursor, latestCursor | default 50, max 200 | cursor or after |
| Usage | usage.get, getForAgent | get-usage, get-agent-usage | none | not cursored; Session top-N default 50, max 200 | dates, Agent, Session, Attribution, grouping |
Lists without cursor fields return everything in one bounded response. No list supports offsets, total counts, or custom sorting.
Session and task-run transcripts add latestCursor:
cursorwalks backward to older messages.afterreads messages newer than a saved position.- Send one or the other, never both.
- When
datais not empty, savelatestCursorand use it as the nextaftervalue, even ifnextCursoris null. - In forward mode, pass a non-null
nextCursorback asafterto finish the current result.latestCursormarks where the next poll starts once you have drained it.
Every userId filter works the same way: leave it out to include everyone,
send "" for tenant-level records, or send a value to select that end user.
userId groups your data; it does not restrict access.
Usage from and to are inclusive UTC dates and must be sent together.
Without them you get the last 30 days ending today, and the range can span at
most 31 days. groupBy defaults to day and also accepts agent, model,
session, and user. sessionId: "" selects stateless turns, which come back
with sessionId: null. Tenant-level buckets in groupBy=user keep
userId: "".
Only memories support full-text search.
Multi-value filters such as status on list tenant chat deliveries take a comma-separated list in one parameter.
Examples
Read every session for an agent, one page at a time:
let cursor: string | undefined;
do {
const page = await client.sessions.list({ agentId, cursor, limit: 100 });
for (const session of page.data) {
console.log(session.id);
}
cursor = page.nextCursor ?? undefined;
} while (cursor);Read a task run's transcript once, save its tail, then poll forward. Only pass
back as after a nextCursor that came from a forward request:
const bootstrap = await client.tasks.runMessages({ taskId, runId, limit: 50 });
for (const message of bootstrap.data) console.log(message);
if (bootstrap.latestCursor === null) {
throw new Error("The task run has no transcript yet");
}
await saveTail(bootstrap.latestCursor);
let after: string | undefined = bootstrap.latestCursor;
do {
const page = await client.tasks.runMessages({
taskId,
runId,
after,
limit: 50,
});
for (const message of page.data) console.log(message);
if (page.latestCursor !== null) await saveTail(page.latestCursor);
after = page.nextCursor ?? undefined;
} while (after);Read tenant-level usage and keep the overall totals apart from the top-N buckets:
const usage = await client.usage.get({
from: "2026-07-01",
to: "2026-07-20",
userId: "",
groupBy: "session",
limit: 25,
});
console.log(usage.totals.requestCount, usage.buckets);Next
- Sessions and turns to continue and reload conversations.
- Tasks and schedules to run agents in the background.
- Usage and quotas to track spend per user.