Skip to main content
Every list returns the same envelope, whatever it is listing:
Two query parameters control it, and they are the same everywhere:
Read total when you need to size a job before you start it, and hasMore to know when to stop. Do not infer the end of a list from a short page: a filtered page can be short and still have more behind it.

Ordering

Every list has a fixed order and none of them lets you change it. Paging is only stable because the order is.

Filtering

There is essentially none, and that is a deliberate cut rather than an omission. GET /v1/matters/{matterId}/documents takes a folderId, and that is the only content filter on the API. There is no search, no date range, no status filter and no sort parameter anywhere. If you need “every matter touched since yesterday”, page the list and filter client-side, or hold your own index keyed on the ids we return.

Why offsets and not cursors

Cursor pagination is better under concurrent writes, and it was deliberately left out. It is a second contract to keep stable, it makes “how many are there” impossible to answer cheaply, and the lists here are firm-sized rather than internet-sized. The tradeoff you inherit: if rows are created while you are paging, a deep offset can skip or repeat a row. For a nightly sync over a few thousand matters that is invisible. If it is not invisible for your workload, page fastest-first with a large limit and reconcile on ids rather than on position.
Last modified on August 19, 2026