GET /connections/{id}/tables and GET /connections/{id}/tables/{table}. Nothing under /records names a source table. Product code reads collections; the estate is the window onto the raw rows a collection is built from.
What a source table is
A source table is one object of a connected system, registered with Nexio and read by the system’s own name for it. Its registration decides everything a read does:
A table registered in a Nexio release is readable on the estate in that release, with no new route and no client change. The registry is fixed in code; no organization defines its own tables.
Why it lives on the connection
A source table belongs to one system at one connection. An organization with two connections on different systems has two different contact tables, so a path that names a source table has to name the connection too. A collection is the opposite: it is one thing, answered from wherever its data lives, and Records resolves that source per connection without the source showing on the wire. Keeping source names out of/records keeps the product surface vendor neutral. When a field moves from one source to another, or a second system of record is connected, the collection keeps its name and shape, and only the estate changes.
Who reads it
The estate is for looking underneath a collection, never for building a product screen on:- Lineage. Each column of the listing names the collection fields it feeds, so a field can be traced to the raw row it came from and back.
- Operators. Support and data work reads the raw rows of one record, under that person’s own authority, to explain what a collection shows.
- Proofs. Parity and lifecycle checks compare collections to the rows they are built from, and count which columns a collection serves.
/records. A column that no collection serves yet is marked estate_only in the listing; the way to make it available to an application is a collection field, not an estate read.
Authority on the estate
The estate never widens what a person can see. Every read resolves the person’s authority first, as every Records read does (Authority and scope), then applies the table’s requirement before any row is read.- A table owned by a policy, an account or a claim is read under that record’s own requirement, and only for a record inside the person’s scope. A record outside it answers 404
NOT_VISIBLE_OR_ABSENT, the same as a record that does not exist. - An accounting table is read only under the accounting dataset. Without it the read answers 403
CAPABILITY_DENIED, reasonDATASET_DENIED. - Direct identifiers (names, email addresses, phone numbers, free text) are served only on a read narrowed to one owner.
- An organization-wide table read for a person-scoped seat answers 200 with no rows and names the table in
unreachable.
List a connection’s tables
GET /connections/{id}/tables returns, in one page sorted by name, every registered table the person may read. A table whose requirement the person’s access policy does not grant is not listed: a seat without the accounting dataset sees no accounting table, exactly as reading one answers 403. {id} is one of your organization’s connection ids, or sandbox for the sandbox seat’s view of it; any other id answers 404 NOT_VISIBLE_OR_ABSENT. The listing reads no rows.
Read a table
GET /connections/{id}/tables/{table} returns one page of the table’s rows.
There is no generic
match= parameter: it answers 400 INVALID_ARGUMENT, reason FILTER_NOT_TAKEN, naming the table’s own match filter. Any other query parameter the table does not take is refused too, so a misspelt filter never widens a read. Send the identity headers on every request, as for any Records read.
The response is the one list envelope:
The sandbox view reads no warehouse, so every table answers 200 with no rows,
source.kind absent and the reason in unreachable.
Example
Read the first two rows of the code dictionary,prcode, a table read whole with no filter. Replace CONNECTION_ID with one of your connection ids.
custcontact hangs off an account, so it takes account_id; company takes its match column as cocode:
{instance}:policy_term:{id} for a policy term and {instance}:customer:{id} for an account. Send them back unchanged.
Pagination and cursors
The table read pages throughpage.next until it is null, the same walk as a register (Walk a whole register safely).
- The cursor is opaque. It records the table and its filters: resend the first page’s owner or match filter with it, unchanged. A different filter answers 400
INVALID_ARGUMENT, reasonCURSOR_FILTER_MISMATCH. - A cursor this API did not issue answers reason
INVALID_CURSOR. A cursor the read can no longer continue from answers reasonCURSOR_EXPIRED(400INVALID_ARGUMENT, or 424SOURCE_UNAVAILABLEwhen the warehouse read can no longer continue from it), as on every Records list: drop it and start again from the first page. - The identity headers are never taken from a cursor. Resend them on every page.
- Each page is a new current read. A row created or changed during a walk can appear on a later page, or be missed.
Errors
Every refusal is an RFC 9457 problem (application/problem+json) with an UPPER_SNAKE code, and a finer cause in reason when there is one.
Authority refusals are on Authority and scope.
Next
List a connection's tables
Endpoint reference.
List a table's rows
Endpoint reference.
Records overview
Collections, the product surface.
Authority and scope
How the person’s rows are bounded.