Skip to main content

Catalogue polling

OpenApp retrieves the product catalogue from an endpoint exposed by the merchant and configured in the control panel. The first retrieval imports the full catalogue; subsequent retrievals are incremental and only return products changed since the last synchronization.

After the initial full sync, OpenApp may also schedule a periodic full refresh during off-peak hours to reconcile any gaps that may have accumulated in incremental syncs.

Request

OpenApp executes a HTTP GET request to the configured endpoint:

GET <catalogue-url>?checkpoint=<checkpoint>&limit=<limit>
ParameterDescription
checkpointAn opaque string (see below) encoding the position in the catalogue from which to continue. Absent on a full synchronization, in which case all products should be returned.
limitThe maximum number of products OpenApp wants to receive in a single page.

Checkpoint format

The checkpoint is a Base64-encoded string containing the updatedAt epoch milliseconds and the id of the last product returned on the previous page, separated by a colon:

Base64("<updatedAt epoch millis>:<last product id>")

Every product must carry an updatedAt value, since OpenApp uses it to construct and decode checkpoints.

Products must be returned in ascending (updatedAt, id) order. The merchant must return all products whose updatedAt is strictly greater than the encoded timestamp, plus all products whose updatedAt equals the encoded timestamp and whose id is lexicographically greater than the encoded id (to handle ties). This ordering ensures the checkpoint reliably advances with each page.

For example, if the last product on the previous page had updatedAt = 2026-06-09T11:48:12.000Z and id = id123 (epoch millis 1781005692000):

MTc4MTAwNTY5MjAwMDppZDEyMw==

So if you configured the value https://shop.example.com/api/openapp/catalogue, the request executed by OpenApp for the second page will be:

GET https://shop.example.com/api/openapp/catalogue?checkpoint=MTc4MTAwNTY5MjAwMDppZDEyMw%3D%3D&limit=500

Response

In response, the merchant returns a page of products together with a nextCheckpoint encoding the position of the last product in the page. The merchant must include nextCheckpoint whenever products is non-empty. OpenApp keeps requesting pages until it receives a page with an empty products array, at which point the last received nextCheckpoint is stored as the checkpoint for the next incremental synchronization.

Products are returned in ascending (updatedAt, id) order. In the example below the delisted product was last updated earliest and therefore comes first; the two active products follow in chronological order.

{
"currency": "PLN",
"products": [
{
"id": "id125",
"updatedAt": "2026-06-07T16:30:00.000Z",
"name": "Discontinued product",
"status": "DELISTED",
"variants": [
{
"id": "id125",
"unitPrice": 4500,
"stock": {
"isAvailable": false
}
}
]
},
{
"id": "id124",
"updatedAt": "2026-06-08T09:15:00.000Z",
"name": "Superb product 2",
"categories": [
"Home",
"Kitchen",
"Cookware"
],
"images": [
"http://cdn.merchant.com/static/products/id124/1"
],
"url": "https://shop.example.com/products/id124",
"status": "ACTIVE",
"variants": [
{
"id": "id124",
"ean": "5901234123464",
"unitPrice": 6000,
"stock": {
"isAvailable": true,
"availableQuantity": 3
},
"measurement": {
"type": "VOLUME",
"quantityValue": 500,
"quantityUnit": "ML"
}
}
]
},
{
"id": "id123",
"updatedAt": "2026-06-09T11:48:12.000Z",
"name": "Superb product",
"description": "A superb product that makes everyday cooking easier.",
"brandName": "SuperBrand",
"categories": [
"Home",
"Kitchen",
"Cookware"
],
"images": [
"http://cdn.merchant.com/static/products/id123/1",
"http://cdn.merchant.com/static/products/id123/2"
],
"url": "https://shop.example.com/products/id123",
"status": "ACTIVE",
"variants": [
{
"id": "id123-red",
"name": "Red",
"ean": "5901234123457",
"unitPrice": 6000,
"originalUnitPrice": 7000,
"stock": {
"isAvailable": true,
"availableQuantity": 5
},
"measurement": {
"type": "WEIGHT",
"quantityValue": 0.3,
"quantityUnit": "KG",
"referenceValue": 100,
"referenceUnit": "G"
}
},
{
"id": "id123-blue",
"name": "Blue",
"ean": "5901234123471",
"unitPrice": 7000,
"stock": {
"isAvailable": true
},
"measurement": {
"type": "WEIGHT",
"quantityValue": 0.3,
"quantityUnit": "KG",
"referenceValue": 100,
"referenceUnit": "G"
}
}
]
}
],
"nextCheckpoint": "MTc4MTAwNTY5MjAwMDppZDEyMw=="
}

Products and variants

Every product carries at least one variant. For products with a single variant, provide exactly one variant - its id may be the same as the product id. Prices, stock, measurement, and barcode live on the variant, not the product.

The variant id is the identifier used across all other APIs: basket retrieval, order placement, the recommendations order feed, the wishlist feed and recommendation responses.

Any change to a variant - price, stock level, EAN - must bump the product's updatedAt, since incremental sync and checkpoints operate at product level.

Stock is expressed as { isAvailable, availableQuantity? }. availableQuantity is only present when the merchant tracks inventory. isAvailable: true with no availableQuantity means the variant is available but inventory is not tracked.

tip

The variant ean, product images, and product brandName fields are optional, but including them significantly improves the recommendation algorithm. Providing these fields is strongly recommended.

If the merchant endpoint fails or times out during a scheduled poll, OpenApp retries on the next synchronization cycle. No merchant action is needed.

info

Products physically deleted from the merchant system cannot appear in an incremental sync response. To let OpenApp learn about the removal, either:

  • Mark the product as DELISTED and keep returning it in incremental responses for as long as the merchant system retains a record of it, or
  • Let OpenApp handle it automatically: during each periodic full refresh, any product previously known to OpenApp that is absent from the merchant's response will be automatically delisted.