A paginated list takes the following query parameters:
To get the next page, send the same request again with
cursor set to the nextCursor you received, and keep every other query parameter unchanged. Stop when hasMore is false.
This page describes API version
2026-10. In 2026-09, lists return the array under a name of their own, such as documents, and most page with page and perPage. In 2026-10, page and perPage return 400 VALIDATION_FAILED. For the differences, see Upgrading to 2026-10.Walk a list
The following samples list every document in the workspace, 100 at a time:SAJN_API_KEY environment variable. Every paginated list has the same shape, so to walk another list, change the path, such as /contacts. Add filters to the first request only; the loops keep them on every page.
A full walk of a large list sends many requests in a short time. To stay within your per-minute limit, see Rate limits and quotas.
Paginated and bounded lists
Lists that grow with your data, such as documents, contacts, folders, and a document’s comments, are paginated as this page describes. Lists that their parent bounds aren’t paginated. They return every item in one response, as{ data, hasMore: false, nextCursor: null }, and don’t take limit or cursor. The following lists are bounded:
- A document’s parties, fields, field values, signatures, files, links, reminders, and delegations.
- A template’s parties and fields.
- The workspace’s roles and permissions.
- The
/api/v1/helperslists, such as countries and currencies.
Get the total
A list returnstotal only when you send include=total, because counting every match takes time. Ask for it on the first page only, when you need it, such as to show a count in your UI:
hasMore, not total.
Sort a list
Most lists return the newest items first. The following lists also takeorderBy and orderDirection, where orderDirection is asc or desc, with a default of desc:
Documents with no
completedAt or expiresAt value sort last in both directions.
A cursor belongs to the sort order it was issued for. A cursor sent with a different orderBy returns 400 VALIDATION_FAILED. To change the order, start again without cursor.
Changes during a walk
The document, form, form submission, event, webhook, webhook delivery, tag, folder, approval request, document activity, and document message lists page by keyset: the cursor marks the last item you received. Items that are created or deleted while you walk never shift the items you haven’t read yet, and everyorderBy works the same way. This makes these lists the right choice for syncing data.
The other paginated lists, such as contacts and templates, page by position. If items are created or deleted while you walk one, items can shift between pages, so a walk can skip or repeat an item. Deduplicate by id.
Sync changes incrementally
To keep a copy of your documents up to date, sort by the time of the last change, oldest first, and start at the newest change you’ve stored:hasMore is false, and then store the largest updatedAt you received for the next run. A document that changes during the walk moves to the end of the list, so the walk still reaches it. updatedAfter includes its boundary, so the next run returns the last document again; deduplicate by id.
Forms and form submissions work the same way with updatedAfter and orderBy=updatedAt. To reconcile webhook events you might have missed, see Replay and reconcile.
Invalid cursors
A cursor is opaque. Don’t parse it, build it, or store it for later use; pass it back unchanged on the next request. A cursor that the API can’t read returns400 VALIDATION_FAILED. To start over, send the request without cursor.
Next steps
- Query parameters: filter formats, such as dates and multi-value filters.
- Rate limits and quotas: the per-minute and daily limits that a full walk counts against.
- Errors: the error shape and every error code.

