Filtering, search and sorting
List endpoints accept a filter query parameter: a MongoDB-style JSON document
that is compiled to a SQL WHERE clause. Pass it as a URL-encoded JSON string on
GET requests, or as a plain object in a JSON request body.
Free-text search
Most list endpoints also accept a search query parameter: a plain string
matched case-insensitively against the record's most identifying fields. A
customer matches on email, name and phone; an order on its number, remote ID,
notes and its customer; a review on its title, body, customer and product. The
same box appears on the corresponding list pages in the dashboard.
Search reaches through associations where a record has little text of its own:
a review request (survey) matches on its order's number and customer, and
loyalty activity matches on the member's customer. search and filter
combine with AND, so a search can be narrowed by a structured filter in the
same request. Matching is a substring containment, not relevance ranking, and
it is backed by trigram indexes so it stays fast on large stores.
Sorting
List endpoints accept a sort query parameter: a field name, prefixed with - to
reverse it. Each endpoint's reference page lists the fields it accepts. Most default
to -createdAt, newest first.
In the dashboard the same choice sits on the column headers. Every list screen can be sorted by its Date column, and starts on it newest first. A few columns people rank by are sortable too: rating on reviews, total and order number on orders, the Activity column on loyalty activity, stamps on stamp cards, name on wishlists, wishlist count on products. Click a header once to sort ascending, again to reverse it. A chevron marks the column in use, and the sort travels in the page address, so a sorted list is something you can bookmark or send to a colleague. The screen's starting order is left out of the address to keep it short. Survey flows are the one screen that starts alphabetically rather than on its date.
Columns that are counted or worked out for each row, such as an order's status or a customer's review count, are not sortable. They would need the whole table read on every page.
Shape
A filter is an object mapping field names to a value or an operator object.
Multiple keys are combined with AND:
In the dashboard the filter bar calls this field Date. It is createdAt on the
wire, so a saved quick filter or a link written before the label changed still works.
A bare value is shorthand for $eq:
Operators
Use an operator object to compare with something other than equality. You cannot mix operators and plain keys in the same object.
Nested JSONB keys
Use a dot path to filter on a key inside a JSONB column. Only $eq is supported
(containment match):
Associations
Filter on a related table's field with a dot path. The endpoint matches rows that have at least one matching associated record:
Use the bare association name with a comparison operator to filter on the count of related records:
Logical operators
Combine or negate sub-filters with $and, $or, and $not:
Paging through a long list
A list endpoint answers with one page and a nextCursor. Pass that value back as
cursor to get the page after it; nextCursor is null once the last page has
been returned. The default page is 50 records.
In the dashboard the same paging sits under the table as a Load more button. It appears only while there are more records to fetch, and each press appends the next page below what is already on screen rather than replacing it. Until the last page arrives the count beside the quick filters reads with a plus, "50+ results", because it counts what is loaded rather than everything that matches.
Errors
Filtering by an unknown field, mixing operators with plain keys, or passing a value
that fails the column's schema returns a 400 validation error describing the
problem. In the dashboard that lands as a panel where the table would be, headed
"Couldn't load this list", with the same message underneath and buttons to try
again or clear the filters.