Skip to content

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 / notnull the 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 a page[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 externalId value can in theory appear for different upstreams. Combine the filter with another attribute (for example the channel) to disambiguate, and always iterate data rather than assuming a single result.