POST /v1/records/query) is the comprehensive filter + sort surface: it can filter any of the 501 filterable dimensions across the 32 record roots and sort on any sortable field. Resource list endpoints (GET /v1/<resource>) accept the same sort + cursor model with a curated subset of filters.
The filter request model
A filter is aFilterConfig: a field, an operator, a value, and a conjunction that joins it to the next rule.
Filters compile to a Hasura
whereClause; you may also pass a raw whereClause for the full Hasura reach. Sorting is orderBy ( field + direction ). See POST /v1/records/query.
Operators by data type
The operator set is gated by the field’s data type — sending an out-of-type operator is rejected.is_null / is_not_null take no value. in takes an array. contains / starts_with / ends_with map to Hasura _ilike (case-insensitive).
Raw whereClause — full Hasura reach
The filters model above is the curated, type-gated layer. For anything it cannot express, POST /v1/records/query accepts a raw whereClause that is passed through to Hasura unchanged (z.object({}).passthrough()), giving you the complete Hasura boolean-expression grammar — workspace row-level security is still enforced, so passthrough never widens your scope.
Boolean connectives
Combine conditions with_and, _or, and _not (arbitrarily nested):
Comparison operators (full set)
A field condition is{ "<field>": { "<op>": <value> } }. The complete Hasura operator set is available through passthrough — a superset of the curated operators above:
How the curated operators compile to Hasura:
eq → _eq, neq → _neq, gt/gte/lt/lte → _gt/_gte/_lt/_lte, in → _in, is_null → _is_null: true, is_not_null → _is_null: false, contains → _ilike "%v%", starts_with → _ilike "v%", ends_with → _ilike "%v".
Filtering across relationships (related / included objects)
You can filter on the fields of a related object — the same relationships you pass toinclude and see under a response’s relationships. How you nest depends on the relationship cardinality, which each resource’s object-reference page lists as to-one or to-many:
To-one relationships (issuer, receiver, document, workspace, …) — nest the relationship name directly, then the field condition. The dot-path field form in the curated model does the same thing:
invoice_items, invoice_transactions, payment_means, …) — you must quantify with _some, _every, or _none; nesting a field directly on an array relation is invalid:
{ "issuer": { "workspace": { "name": { "_eq": "Acme" } } } }), and each hop must be one of the resource’s documented relationships. Workspace row-level security still applies at every hop, so a related-object filter never widens your tenant scope.
Composite fields
Composite columns (composite_*, e.g. composite_total_amount_currency) are virtual — they are reconstructed from underlying source fields at response time and cannot be filtered or sorted directly. Filter or sort on the source field instead (each composite’s object-reference entry lists its sources): to order by the invoice total, use grand_total, not composite_total_amount_currency.
Filtering — example
whereClause are also accepted (_and, _or, _ilike, _gte, …).
Sorting
orderBy takes a field and a direction (asc / desc). Sortability is gated per field (JSONB and computed columns may be non-sortable); an unsortable field is rejected. A stable tie-break is applied automatically. List endpoints accept sort (comma-separated, - prefix for descending) for the same effect.
Sorting across relationships (related / included objects)
To-one relationships — order by a related field with a dot path; it compiles to a nested Hasuraorder_by:
invoice_items.amount is ambiguous (each invoice has many items), so a bare array-relation sort is rejected (the sort is dropped and the default order applies — no error). To order by a to-many relation, the field must expose an aggregate sort proxy (configured per field, e.g. subtasks_aggregate.min.created_at), which sorts by the aggregate (min/max/sum) over the related rows. Where a proxy exists it is applied automatically when you sort the corresponding composite; where one does not, that relation is simply not sortable.
The full per-record dimension catalog
The complete map of which dimensions are filterable on each of the 32 roots, their data types, and the resulting operator set is maintained as the Atlas matrix — seedocs/wireframes/table-views/matrix.html (the filter / sort / view-management reference). Each root’s filterable fields are also visible in its records data model. This page is the contract (operators + model); the matrix is the exhaustive per-field index.
Chat and MCP apply filters through this same model — the records query is the single filter engine behind every surface.