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>
| Parameter | Description |
|---|---|
checkpoint | An 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. |
limit | The 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.
- Response
- Schema
{
"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=="
}
currencystringRequiredThe currency of all prices in this response.
productsarray of CatalogueProductRequiredShow child parametersHide child parameters11
idstringRequiredThe unique ID of the product. Groups its variants. Any change to a variant must bump this product's updatedAt.
maxLength: 36
updatedAtstringRequiredThe moment the product or any of its variants was last changed in the merchant system.
format: date-time
namestringRequiredThe product name.
maxLength: 255
descriptionstringOptionalThe product description. Plain text, no HTML.
maxLength: 5000
descriptionHtmlstringOptionalThe product description as HTML. Provide description, descriptionHtml, or both.
brandNamestringOptionalThe brand of the product. Optional, but strongly recommended - significantly improves recommendation quality.
maxLength: 255
categoriesarray of stringOptionalThe category path of the product from the most generic to the most specific category, i.e. ["Home", "Kitchen", "Cookware"].
imagesarray of stringOptionalThe URLs of the product images. Optional, but strongly recommended - significantly improves recommendation quality. Variants may override with their own images.
urlstringOptionalThe URL of the product page in the webshop.
statusenumOptionalThe listing status of the product. DELISTED informs OpenApp the product was permanently removed from the catalogue. Defaults to ACTIVE.
Possible values: ACTIVEDELISTED
variantsarray of CatalogueVariantRequiredThe sellable variants of this product. Every product has at least one variant. For products without real variants (a single SKU), provide exactly one variant - its id may be the same as the product id. Prices, stock, measurement and barcode live on variants.
minItems: 1
Show child parametersHide child parameters8
idstringRequiredThe unique ID of the variant. This is the product ID used in basket retrieval, order placement, the recommendations order feed, the wishlist feed and recommendation responses. Must be consistent across all APIs.
maxLength: 36
namestringOptionalThe variant name, e.g. 'Red / M'. When absent, the product name applies.
maxLength: 255
eanstringOptionalThe ean (or other barcode) of the variant. Optional, but strongly recommended - significantly improves recommendation quality.
maxLength: 36
unitPriceintegerRequiredThe current price of a single item. Price is expressed as the number (integer) of 1/100s of the price.
minimum: 0
originalUnitPriceintegerOptionalThe original (before discount) price of a single item. Price is expressed as the number (integer) of 1/100s of the price. Omit when the variant is not discounted.
minimum: 0
stockobjectRequiredThe stock status of the variant.
Show child parametersHide child parameters2
isAvailablebooleanRequiredWhether the variant is available for purchase.
availableQuantityintegerOptionalThe number of items available in stock. Omit when inventory is not tracked - isAvailable: true with no availableQuantity means available with untracked inventory.
minimum: 0
measurementone of: WeightMeasurement, VolumeMeasurementOptionalThe net content of a single item. Used for unit pricing display and replenishment predictions.
Show child parametersHide child parameters2
WeightMeasurementobjectShow child parametersHide child parameters5
typeliteral "WEIGHT"RequiredIdentifies this as a weight measurement.
Value: WEIGHT
quantityValuenumberRequiredThe net weight amount.
exclusiveMinimum: 0
quantityUnitenumRequiredThe unit of quantityValue.
Possible values: GKGMG
referenceValuenumberOptionalReference amount for unit pricing display, e.g. 100 (with referenceUnit G) to show a price-per-100g label. Required when referenceUnit is set.
exclusiveMinimum: 0
referenceUnitenumOptionalUnit of the reference amount. Required when referenceValue is set.
Possible values: GKGMG
VolumeMeasurementobjectShow child parametersHide child parameters5
typeliteral "VOLUME"RequiredIdentifies this as a volume measurement.
Value: VOLUME
quantityValuenumberRequiredThe net volume amount.
exclusiveMinimum: 0
quantityUnitenumRequiredThe unit of quantityValue.
Possible values: MLCLLM3
referenceValuenumberOptionalReference amount for unit pricing display, e.g. 100 (with referenceUnit ML) to show a price-per-100ml label. Required when referenceUnit is set.
exclusiveMinimum: 0
referenceUnitenumOptionalUnit of the reference amount. Required when referenceValue is set.
Possible values: MLCLLM3
imagesarray of stringOptionalVariant-specific image URLs. When absent, the product images apply.
nextCheckpointstring or nullOptionalAn opaque Base64-encoded string encoding the position of the last product in this page (updatedAt epoch millis + id). OpenApp sends this value as the checkpoint parameter of the next request. Must be present whenever products is non-empty. Omit or set to null only on the final empty-products page that signals end of sync.
maxLength: 255
Raw JSON Schema
{
"description": "A page of the merchant product catalogue returned on catalogue polling",
"additionalProperties": false,
"type": "object",
"properties": {
"currency": {
"description": "The currency of all prices in this response.",
"type": "string",
"title": "currency"
},
"products": {
"type": "array",
"items": {
"$ref": "#/definitions/CatalogueProduct"
},
"title": "products"
},
"nextCheckpoint": {
"description": "An opaque Base64-encoded string encoding the position of the last product in this page (updatedAt epoch millis + id). OpenApp sends this value as the checkpoint parameter of the next request. Must be present whenever products is non-empty. Omit or set to null only on the final empty-products page that signals end of sync.",
"maxLength": 255,
"type": [
"string",
"null"
],
"title": "nextCheckpoint"
}
},
"required": [
"currency",
"products"
],
"definitions": {
"CatalogueProduct": {
"title": "CatalogueProduct",
"type": "object",
"properties": {
"id": {
"description": "The unique ID of the product. Groups its variants. Any change to a variant must bump this product's updatedAt.",
"maxLength": 36,
"type": "string",
"title": "id"
},
"updatedAt": {
"description": "The moment the product or any of its variants was last changed in the merchant system.",
"format": "date-time",
"type": "string",
"title": "updatedAt"
},
"name": {
"description": "The product name.",
"maxLength": 255,
"type": "string",
"title": "name"
},
"description": {
"description": "The product description. Plain text, no HTML.",
"maxLength": 5000,
"type": "string",
"title": "description"
},
"descriptionHtml": {
"description": "The product description as HTML. Provide description, descriptionHtml, or both.",
"type": "string",
"title": "descriptionHtml"
},
"brandName": {
"description": "The brand of the product. Optional, but strongly recommended - significantly improves recommendation quality.",
"maxLength": 255,
"type": "string",
"title": "brandName"
},
"categories": {
"description": "The category path of the product from the most generic to the most specific category, i.e. [\"Home\", \"Kitchen\", \"Cookware\"].",
"type": "array",
"items": {
"maxLength": 255,
"type": "string"
},
"title": "categories"
},
"images": {
"description": "The URLs of the product images. Optional, but strongly recommended - significantly improves recommendation quality. Variants may override with their own images.",
"type": "array",
"items": {
"type": "string"
},
"title": "images"
},
"url": {
"description": "The URL of the product page in the webshop.",
"type": "string",
"title": "url"
},
"status": {
"description": "The listing status of the product. DELISTED informs OpenApp the product was permanently removed from the catalogue. Defaults to ACTIVE.",
"enum": [
"ACTIVE",
"DELISTED"
],
"type": "string",
"title": "status"
},
"variants": {
"description": "The sellable variants of this product. Every product has at least one variant. For products without real variants (a single SKU), provide exactly one variant - its id may be the same as the product id. Prices, stock, measurement and barcode live on variants.",
"minItems": 1,
"type": "array",
"items": {
"$ref": "#/definitions/CatalogueVariant"
},
"title": "variants"
}
},
"required": [
"id",
"updatedAt",
"name",
"variants"
]
},
"CatalogueVariant": {
"title": "CatalogueVariant",
"type": "object",
"properties": {
"id": {
"description": "The unique ID of the variant. This is the product ID used in basket retrieval, order placement, the recommendations order feed, the wishlist feed and recommendation responses. Must be consistent across all APIs.",
"maxLength": 36,
"type": "string",
"title": "id"
},
"name": {
"description": "The variant name, e.g. 'Red / M'. When absent, the product name applies.",
"maxLength": 255,
"type": "string",
"title": "name"
},
"ean": {
"description": "The ean (or other barcode) of the variant. Optional, but strongly recommended - significantly improves recommendation quality.",
"maxLength": 36,
"type": "string",
"title": "ean"
},
"unitPrice": {
"description": "The current price of a single item. Price is expressed as the number (integer) of 1/100s of the price.",
"minimum": 0,
"type": "integer",
"title": "unitPrice"
},
"originalUnitPrice": {
"description": "The original (before discount) price of a single item. Price is expressed as the number (integer) of 1/100s of the price. Omit when the variant is not discounted.",
"minimum": 0,
"type": "integer",
"title": "originalUnitPrice"
},
"stock": {
"description": "The stock status of the variant.",
"$ref": "#/definitions/VariantStock",
"title": "stock"
},
"measurement": {
"description": "The net content of a single item. Used for unit pricing display and replenishment predictions.",
"oneOf": [
{
"$ref": "#/definitions/WeightMeasurement"
},
{
"$ref": "#/definitions/VolumeMeasurement"
}
],
"title": "measurement"
},
"images": {
"description": "Variant-specific image URLs. When absent, the product images apply.",
"type": "array",
"items": {
"type": "string"
},
"title": "images"
}
},
"required": [
"id",
"unitPrice",
"stock"
]
},
"VariantStock": {
"title": "VariantStock",
"type": "object",
"properties": {
"isAvailable": {
"description": "Whether the variant is available for purchase.",
"type": "boolean",
"title": "isAvailable"
},
"availableQuantity": {
"description": "The number of items available in stock. Omit when inventory is not tracked - isAvailable: true with no availableQuantity means available with untracked inventory.",
"minimum": 0,
"type": "integer",
"title": "availableQuantity"
}
},
"required": [
"isAvailable"
]
},
"WeightMeasurement": {
"title": "WeightMeasurement",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"description": "Identifies this as a weight measurement.",
"const": "WEIGHT",
"type": "string",
"title": "type"
},
"quantityValue": {
"description": "The net weight amount.",
"exclusiveMinimum": 0,
"type": "number",
"title": "quantityValue"
},
"quantityUnit": {
"description": "The unit of quantityValue.",
"enum": [
"G",
"KG",
"MG"
],
"type": "string",
"title": "quantityUnit"
},
"referenceValue": {
"description": "Reference amount for unit pricing display, e.g. 100 (with referenceUnit G) to show a price-per-100g label. Required when referenceUnit is set.",
"exclusiveMinimum": 0,
"type": "number",
"title": "referenceValue"
},
"referenceUnit": {
"description": "Unit of the reference amount. Required when referenceValue is set.",
"enum": [
"G",
"KG",
"MG"
],
"type": "string",
"title": "referenceUnit"
}
},
"required": [
"type",
"quantityValue",
"quantityUnit"
]
},
"VolumeMeasurement": {
"title": "VolumeMeasurement",
"type": "object",
"additionalProperties": false,
"properties": {
"type": {
"description": "Identifies this as a volume measurement.",
"const": "VOLUME",
"type": "string",
"title": "type"
},
"quantityValue": {
"description": "The net volume amount.",
"exclusiveMinimum": 0,
"type": "number",
"title": "quantityValue"
},
"quantityUnit": {
"description": "The unit of quantityValue.",
"enum": [
"ML",
"CL",
"L",
"M3"
],
"type": "string",
"title": "quantityUnit"
},
"referenceValue": {
"description": "Reference amount for unit pricing display, e.g. 100 (with referenceUnit ML) to show a price-per-100ml label. Required when referenceUnit is set.",
"exclusiveMinimum": 0,
"type": "number",
"title": "referenceValue"
},
"referenceUnit": {
"description": "Unit of the reference amount. Required when referenceValue is set.",
"enum": [
"ML",
"CL",
"L",
"M3"
],
"type": "string",
"title": "referenceUnit"
}
},
"required": [
"type",
"quantityValue",
"quantityUnit"
]
}
},
"$schema": "http://json-schema.org/draft-07/schema#"
}
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.
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.
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
DELISTEDand 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.