Skip to main content
A register is a paged, filterable list over one kind of record, sorted and filtered on the server and bounded to the rows the person may open. Where a family read returns one dataset as it is stored, a register composes a list screen: one row per record, with the columns a list needs. Most registers also return the numbers a list screen needs beside the page: a total that matches the filters, facet counts for the filter controls, and sums over the filtered set. Registers are dedicated routes under /api/v1/records/registers/. The set of registers is fixed and defined per connected system type, and each register has its own filters, sort keys and page sizes. This page covers the mechanics every register shares.

How a register is read

  1. Send the first request with the filters, the sort, the selection (Authority and scope) and the identity headers.
  2. Nexio resolves the person’s scope, applies it, then applies your filters inside it.
  3. The response carries data (the page), page (the continuation), the numbers the register computes, and the serving and scope blocks.
  4. Follow page.next_cursor to the next page, or send offset to jump to a row number on a register that accepts it.
Every register takes limit, cursor and connection_id. Each register sets its own default and maximum page size, and either clamps a limit above its maximum or refuses it with 400 invalid_request.

Two ways to page

  • Cursor. Follow page.next_cursor until it is null. This is the right way to read a whole register. On most registers the cursor is a keyset cursor: each page continues after the last row the previous page returned. A register may instead use an offset cursor, which stores the next row number. Each page is a new current read (When the data changes during a walk).
  • Offset (page jump). On a register that accepts it, send offset to jump to a row number, for a numbered-page screen. The largest offset is 1,000,000. When you send offset, the response echoes the applied window in page.offset and page.page_size. next_cursor is still returned and still says whether more rows exist.
Sending both offset and cursor answers 400 invalid_request: a cursor already fixes the position.

What a cursor fixes, and what you resend

A cursor is opaque. It carries the continuation position and the filters it fixes. It does not hold later pages to the time of the first page, and it never carries permissions: authority is resolved again on every page. Which filters a cursor fixes differs by register. On most registers the first page’s filters and sort are fixed by the cursor, and sending one of them again with the cursor answers 400 cursor_filter_mismatch. Some registers instead record the filters in the cursor and compare them, so you resend the same values; a different value answers 400 cursor_filter_mismatch. The rule to remember: the identity headers, connection_id, mine and book are never inside a cursor. Resend them on every page.

Page, totals and facets

  • page.limit is the number of rows on this page, not the limit you asked for, on most registers; one register echoes the effective limit instead.
  • page.next_cursor is null on the last page. An empty data array with a null cursor is a complete, empty answer.
  • total counts every row that matches the filters, before paging.
  • facets counts rows per filter value, for the filter controls. The total and the facets are counted over the same authorized base population, the rows the person may read. The total applies every filter. A facet does not always: each register defines its facet filters, and a facet usually leaves out its own dimension, so choosing one value still shows the counts for the others. Read facet counts as “what you would get if you picked this”, not as a breakdown of the total.
  • slice is arithmetic over the filtered set, and totals, where a register sends it, is the person’s whole population under the owner and office filters.
  • Numbers are exact decimal text, for example "48250.00". Parse them with a decimal library. A sum with nothing to add reads null, not 0.
Where a register computes its page and its numbers in one warehouse statement, they describe the same data. Where it uses separate statements, even concurrent ones in the same request, a source change between them can make the numbers and the page disagree.

What complete means for a register

The full outcome model is on Completeness and errors.

Walk a whole register safely

  1. Send the first request with every filter, the sort, the selection and the identity headers.
  2. For each next page, send cursor plus only what the register’s resend rule says. Never rebuild the filters from the previous page’s rows.
  3. Stop when page.next_cursor is null.
  4. On 409 cursor_expired, start again from the first page. The cursor no longer matches how the register is served. A current-read cursor has no age limit of its own.
  5. On 503 book_unavailable with reason busy, wait for Retry-After seconds and resend the same page. On other book_unavailable reasons, see Completeness and errors.
The same walk applies to the generic family read, whose cursor fixes the first page’s family and key filter. The example walks a whole family: rawams_vocabulary/prcode, the code dictionary of the connected system, which takes no key filter.
The last page of the walk has fewer rows than the limit and a null cursor:
200 OK

When the data changes during a walk

No register holds a walk to one instant. Each page reads its sources again at the time of that page’s request, then continues in the same sort order: after the last row the previous page returned, or, on a register with an offset cursor, at the next row number. So a source change during a walk can show up in the pages that follow:
  • A row created, or changed so that it now sorts after the cursor position, can appear on a later page.
  • A row changed so that it sorts before the cursor position can be missed; one that moves from before to after it can appear twice.
  • A row that stops matching the filters, or leaves the person’s scope, is absent from later pages.
  • On a register that pages by row number, a row added or removed before the cursor position shifts every row after it. An unchanged row can then appear twice or be skipped.
  • total, facets and the other numbers are computed again on each page, so they can differ from page to page.
For a consistent count, read the numbers from one page. To pick up changes made during a walk, start a new walk.

Cursor and paging errors

Next

Completeness and errors

Tell an empty list from an unavailable one.

Families and the generic read

Read any registered family by name.
Last modified on September 25, 2026