Pagination & Filtering
Every collection endpoint in the HotelPlatform APIs (for example GET /reservations,
GET /units, GET /configurations) accepts the same query-string conventions for
filtering, sorting and cursor-based pagination. They follow the
JSON:API bracket syntax and are parsed centrally by the shared
SDK, so the behaviour is identical across services.
All examples assume the JSON:API media type:
Accept: application/vnd.api+json
Filtering
Filter a collection with one or more filter[...] parameters. Multiple filters are
combined with logical AND.
filter[<field>]=<value> # implicit equality
filter[<field>][<operator>]=<value> # explicit operator
filter[code]=WL is therefore identical to filter[code][eq]=WL.
Supported operators
| Operator | Meaning | Example |
|---|---|---|
eq |
equals (default) | filter[status][eq]=confirmed |
ne |
not equal | filter[status][ne]=cancelled |
lt |
less than | filter[arrival][lt]=2026-06-01 |
lte |
less than or equal | filter[arrival][lte]=2026-06-01 |
gt |
greater than | filter[departure][gt]=2026-06-10 |
gte |
greater than or equal | filter[departure][gte]=2026-06-10 |
like |
case-insensitive contains | filter[lastName][like]=mei |
isnull |
field is null | filter[unitId][isnull]=true |
notnull |
field is not null | filter[unitId][notnull]=true |
A few rules to keep in mind:
- Nested fields use dot notation, e.g.
filter[profile.lastName][like]=smith. - Fields the resource marks as non-filterable are rejected with
400 Bad Request. - An unknown operator is rejected with
400 Bad Request; the error lists the supported operators. - For
isnull/notnullthe value is only a placeholder (any value works); the presence of the operator is what matters.
Sorting
Use sort with a comma-separated list of fields. Prefix a field with - for
descending order.
sort=externalId # ascending
sort=-createdAt # descending
sort=-createdAt,externalId # multiple keys, evaluated left to right
Cursor pagination
The platform uses cursor-based pagination rather than page numbers. A cursor is
simply the id (UUID) of a resource you already received; you ask for the rows that
come after or before it.
| Parameter | Description |
|---|---|
page[size] |
Number of items per page. Defaults to 10; values above the service maximum (or <= 0) are rejected with 400. |
page[after] |
Return the page of items after the resource with this id. |
page[before] |
Return the page of items before the resource with this id. |
Offset parameters such as
page[number]and apage[total]count are not supported. Cursor pagination deliberately trades a total record count for stable, efficient paging over large tables.
You normally never build cursors by hand — list responses carry the links to follow:
links.self— the current page.links.next— the next page. Only present when more rows exist.links.prev— the previous page. Only present when the request itself used a cursor.
Walking through a collection
Request the first page:
GET /reservations?page[size]=2&sort=-createdAt
Accept: application/vnd.api+json
{
"data": [
{ "type": "reservation", "id": "9f1c2d3e-0001-4a00-9b00-000000000001", "externalId": "res-852963-1" },
{ "type": "reservation", "id": "9f1c2d3e-0002-4a00-9b00-000000000002", "externalId": "res-852963-2" }
],
"links": {
"self": "/reservations?page[size]=2&sort=-createdAt",
"next": "/reservations?page[size]=2&sort=-createdAt&page[after]=9f1c2d3e-0002-4a00-9b00-000000000002"
}
}
Follow links.next to get the next page. When links.next is absent you have
reached the end of the collection.
Example: fetch a reservation by its PMS reservation id
Reservations that originate in (or are mirrored from) a Property Management System
carry the third-party identifier in the externalId attribute — for example
res-852963-1. To look a reservation up by that id, filter the collection on
externalId:
GET /reservations?filter[externalId]=res-852963-1
Accept: application/vnd.api+json
curl -G 'https://hotelplatform.io/api/v2/reservations' \
--data-urlencode 'filter[externalId]=res-852963-1' \
-H 'Accept: application/vnd.api+json' \
-H 'X-Tenant-ID: <your-tenant>'
The response is a JSON:API list document. externalId is unique per integration,
so you normally get exactly one item — read data[0]:
{
"data": [
{
"type": "reservation",
"id": "558112a0-c6e0-11eb-b8bc-0242ac130003",
"externalId": "res-852963-1",
"stays": [ { "type": "stay", "id": "..." } ],
"profiles": [ { "type": "profile", "id": "..." } ]
}
],
"links": {
"self": "/reservations?filter[externalId]=res-852963-1"
}
}
Once you have the HotelPlatform id, fetch the full resource (with its related stays
and profiles) directly:
GET /reservations/558112a0-c6e0-11eb-b8bc-0242ac130003
Accept: application/vnd.api+json
Tip: if you integrate more than one channel or PMS, the same
externalIdvalue can in theory appear for different upstreams. Combine the filter with another attribute (for example the channel) to disambiguate, and always iteratedatarather than assuming a single result.