Skip to content

Pagination and filters

Cursor pages, sparse fieldsets, the filter grammar and sorting, with the order coded lists always keep.

Every list operation returns a page, takes the same query parameters, and keeps a documented default order.

Cursor pages

A page has data and next_cursor. Pass next_cursor back as cursor for the next page; it is null on the last page.

ParameterMeaning
limitPage size, 1 to 200. The default is 50.
cursorThe next_cursor of the previous page. Opaque: never build one yourself.

Cursors stay correct while rows are added, unlike offsets. The SDKs iterate every page for you:

for await (const record of xos.records.listAll({ filter: { record_state: { eq: "Blocked" } } })) {
  console.log(record.title);
}
for record in xos.records.list_all(filter={"record_state": {"eq": "Blocked"}}):
    print(record["title"])

Sparse fieldsets

fields=id,title,record_state returns only those top-level fields. id is always returned. Smaller responses are faster on large lists.

Filters

Filters use filter[field][op]=value. Conditions on different fields combine with AND.

OperatorMatches
eq, neEqual, not equal
lt, lte, gt, gteLess than, at most, greater than, at least
inAny of a comma-separated list
is_nulltrue for empty, false for set
containsText containing the value
curl -G "$XOS_BASE_URL/records" -H "Authorization: Bearer $XOS_API_KEY" \
  --data-urlencode "filter[record_state][eq]=Blocked" \
  --data-urlencode "filter[due_at][lte]=2026-11-01T00:00:00Z"

With the CLI, write --filter record_state.eq=Blocked --filter due_at.lte=2026-11-01T00:00:00Z.

Sorting

sort=starts_at,-updated_at sorts by each key in turn; a leading - sorts descending. Without sort, each list uses the default order stated in its reference entry. Coded lists, such as departments, phases and GL accounts, always return in numeric code order.

Canon caching

Canon changes only when a new canon generation ships, so canon reads can be cached for good. Every GET /canon/... response carries XOS-Canon-Generation. Add generation=<n> to the same request and the response is cached with Cache-Control: private, max-age=31536000, immutable: a URL with a generation always means that generation. A request for any other generation is redirected (307) to the same URL at the current generation, so a cache never serves stale canon. Without generation, the response is revalidated on every use, and Content-Location names the cacheable URL.

curl -i -G "$XOS_BASE_URL/canon/departments" -H "Authorization: Bearer $XOS_API_KEY" \
  --data-urlencode "generation=1"