Filtering and pagination
How every list endpoint works - the filter-paginated request, sorting, field selection and paging.
Large lists are never returned by a plain GET. Every searchable resource has a POST …/filter-paginated endpoint instead, for example POST /api/products/filter-paginated or POST /api/documents/expedition/delivery-notes/filter-paginated. It is a POST only because the search criteria travel in the body. It changes nothing on the server and is safe to repeat.
All of these endpoints share one request shape and one response shape. Only the filter object and the rows in data differ.
Request
{
"filter": {
"basic": {
"dateFrom": "2026-10-01T00:00:00",
"dateTo": "2026-10-31T23:59:59",
"customerId": [1042],
"documentStateId": [2, 3]
}
},
"pageNumber": 1,
"pageSize": 100,
"orderBy": ["dateOfDocument desc", "id"],
"limitFields": ["id", "internalNumber", "dateOfDocument", "customerName", "totalPrice"]
}| Field | Type | Meaning |
|---|---|---|
filter | object | Search criteria. The shape is different for every endpoint; its reference page lists the fields. Every field is optional, and the ones you set are combined with AND. An array field matches any of its values. |
pageNumber | integer | Page to return, starting at 1. |
pageSize | integer | Rows per page. See the warning below. |
orderBy | string[] | Sort order, most significant first. Each entry is a field name, optionally followed by a space and desc. |
limitFields | string[] | Names of the fields you want back. Leave it out to get all of them. |
Always set pageSize
If you leave pageSize out, the API returns every matching row in one response. -1 asks for the same thing
explicitly. On a real catalog or a few years of documents that is a very large response. Pick a page size that suits
the data and page through: 100 to 500 rows when you read whole rows, up to a few thousand when limitFields keeps
each row to a handful of fields.
Sorting
orderBy takes the names of the fields in the response rows. Each entry must be one word, or one word plus a direction: "totalPrice", "totalPrice desc". Formally, an entry has to match ^\w+(\s+\w+)?$. Anything else, such as a comma-separated list in one string, is rejected.
Always sort by something unique last, such as id. Without a stable order, rows can move between pages while you read them.
Selecting fields
limitFields cuts the response down to the columns you name, which makes large syncs much faster. A name the endpoint does not know is rejected, and the error message says which one.
When you use limitFields, rely only on the fields you asked for. Ignore everything else in the row.
Response
{
"data": [
{
"id": 77120,
"internalNumber": 2600418,
"dateOfDocument": "2026-10-09T00:00:00",
"customerName": "Example Retail s.r.o.",
"totalPrice": 12450.5
}
],
"currentPage": 1,
"totalPages": 3,
"totalCount": 212,
"pageSize": 100,
"hasPreviousPage": false,
"hasNextPage": true
}| Field | Meaning |
|---|---|
data | The rows of this page. |
currentPage | The page you asked for. |
totalPages | Number of pages for this filter and page size. |
totalCount | Number of rows that match the filter, across all pages. |
pageSize | Rows per page. |
hasPreviousPage, hasNextPage | Whether there is a page before or after this one. |
Loop while hasNextPage is true.
When a list request is rejected
Three mistakes in the request make a list endpoint answer 400 Bad Request. The message tells you which one:
| Mistake | Message in messages |
|---|---|
A pageNumber beyond totalPages, unless the result is empty | Page number is too high, max is: 3 |
A name in limitFields that the endpoint does not know | The field 'colour' does not exists |
An orderBy entry that is not a word, or a word plus a direction | Invalid order-by field: 'id; name' |
Do not retry these. Read messages and fix the request. See Errors for the error format.
Fields differ between instances
The list columns come from a database view the customer can change
The rows of a filter-paginated endpoint are read from a SQL view inside the WinShop database, and a customer can
have that view customised. The reference shows the columns of the demo instance. If a name in orderBy or
limitFields is rejected on one instance but works on another, the column probably does not exist there. Ask the
customer or WinShop support, and keep the field lists of your integration configurable.
Full example
List the delivery notes of one customer for October 2026, newest first, 100 per page. A delivery note is the document that records goods leaving a warehouse for a customer; see Documents. documentStateId is the customer-defined state of the document.
curl -X POST https://<your-instance>/api/documents/expedition/delivery-notes/filter-paginated \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": {
"basic": {
"dateFrom": "2026-10-01T00:00:00",
"dateTo": "2026-10-31T23:59:59",
"customerId": [1042],
"documentStateId": [2, 3]
}
},
"pageNumber": 1,
"pageSize": 100,
"orderBy": ["dateOfDocument desc", "id"],
"limitFields": ["id", "internalNumber", "dateOfDocument", "customerName", "totalPrice"]
}'The filter fields and the full list of row fields are on the reference page: Search delivery notes.