Skip to content
innovorder
⌘K

Commercial rules

Promotional Banners

Promotional banners are image-based calls to action displayed on KIOSK or Web Ordering (CEL). You can manage local restaurant banners, distribute brand banners to several restaurants, expose the active display feeds to guest applications, and report engagement.

Successful responses other than deletion use the standard envelope shown in every example:status, code,message, and data. The message is localized; integrations should use the HTTP status and code. Request schemas using Joi strip unknown JSON properties before the controller receives them.

Access and terminology

Endpoint groupAuthentication and allowed roles
Restaurant banner CRUD, duplicate, and per-restaurant toggleBearer authentication required; Admin, Brand, or Restaurant.
Brand banner CRUD, carousel settings, targets, CEL products, and pickup schedulesBearer authentication required; Admin or Brand.
Active restaurant feed, active food-court feed, and event trackingGuest-accessible; no bearer token is required. The ACL also permits Admin, Brand, Restaurant, Kiosk, and Customer roles.
  • channelId is 1 for KIOSK and 2 for WEB/CEL.
  • placement is restaurants for per-restaurant display orfoodcourt_home for the brand-wide food-court landing page.
  • Local banners store a category or product ID in linkTargetId. Brand banners store a reusable reference in linkTargetRef: a product SKU or a category name.

Restaurant banner management

These endpoints manage banners owned directly by a restaurant. The list also includes brand banners targeted to that restaurant; their returned isActive value is the target restaurant’s effective activation state.

GET/promotional_banners/restaurant/{restaurantId}List restaurant banners

Requires Bearer authentication and an Admin, Brand, or Restaurant role. Returns local banners plus brand banners targeted to the restaurant. For a brand banner, isActive is the activation state of that restaurant target.

Parameters

NameTypeRequiredDescription
restaurantIdintegerYesRestaurant identifier (path parameter).
sortstringNoOptional query parameter. Use `name`, `status`, `linkType`, or `created_at`, as `field:asc`, `field:desc`, `-field`, or a comma-separated combination. Unknown fields are ignored; default order is created_at ascending.

Response

json
{
  "status": 200,
  "code": "list_promotional_banners_succeed",
  "message": "Promotional banners fetched successfully",
  "data": [
    {
      "promotionalBannerId": 901,
      "name": "Lunch special",
      "imageId": 501,
      "isActive": true,
      "linkType": "product",
      "linkTargetId": 5001,
      "linkTargetRef": null,
      "channelId": 1,
      "placement": "restaurants",
      "restaurantId": 42,
      "brandId": null,
      "created_at": "2026-07-11T09:00:00.000Z",
      "updated_at": "2026-07-11T09:15:00.000Z",
      "deleted_at": null,
      "image": {
        "imageId": 501,
        "fullUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg"
      }
    },
    {
      "promotionalBannerId": 902,
      "name": "Brand summer offer",
      "imageId": 502,
      "isActive": true,
      "linkType": "product",
      "linkTargetId": null,
      "linkTargetRef": "SUMMER-BURGER",
      "channelId": 1,
      "placement": "restaurants",
      "restaurantId": null,
      "brandId": 7,
      "created_at": "2026-07-10T09:00:00.000Z",
      "updated_at": "2026-07-11T09:15:00.000Z",
      "deleted_at": null,
      "image": {
        "imageId": 502,
        "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
      },
      "promotional_banner_brands": [
        {
          "promotionalBannerId": 902,
          "restaurantId": 42,
          "isActive": true,
          "created_at": "2026-07-10T09:00:00.000Z",
          "updated_at": "2026-07-11T09:15:00.000Z"
        },
        {
          "promotionalBannerId": 902,
          "restaurantId": 43,
          "isActive": false,
          "created_at": "2026-07-10T09:00:00.000Z",
          "updated_at": "2026-07-11T09:15:00.000Z"
        }
      ]
    }
  ]
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_promotional_banners_succeed"Stable application success code for this operation.
messagestringYes"Promotional banners fetched successfully"Localized human-readable success message. Do not use this value for program logic.
dataarrayYes[…]Local and targeted brand banner records.
data[]objectNot specified{…}Endpoint-specific response payload.
data[].promotionalBannerIdintegerYes901Unique identifier of the promotional banner.
data[].namestringYes"Lunch special"Human-readable banner name.
data[].imageIdintegerYes501Identifier of the image selected for the banner.
data[].isActivebooleanYestrueFor a local banner, its own activation state. For a brand banner, the activation state of this requested restaurant target.
data[].linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data[].linkTargetIdinteger | nullYes5001Local category/product target ID when known. Brand banners normally use linkTargetRef instead.
data[].linkTargetRefstring | nullYesnullBrand-level target reference (SKU for products or category name for categories), or null when unused.
data[].channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data[].placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data[].restaurantIdinteger | nullYes42Owning restaurant for a local banner; null for a brand-scoped banner.
data[].brandIdinteger | nullYesnullOwning brand for a brand-scoped banner; null for a local banner.
data[].created_atstringYes"2026-07-11T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data[].updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data[].deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data[].imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data[].image.imageIdintegerNo501Identifier of the resolved image.
data[].image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the resolved banner image.
data[].promotional_banner_brandsarrayNoNot providedPer-restaurant target rows. This property is present for brand banners in list responses, not local banners.
data[].promotional_banner_brands[]objectNoNot providedOne brand-banner assignment to a restaurant.
data[].promotional_banner_brands[].promotionalBannerIdintegerNoNot providedIdentifier of the brand banner assigned to the restaurant.
data[].promotional_banner_brands[].restaurantIdintegerNoNot providedIdentifier of the target restaurant.
data[].promotional_banner_brands[].isActivebooleanNoNot providedActivation state for this target restaurant.
data[].promotional_banner_brands[].created_atstring | nullNoNot providedISO 8601 timestamp at which the target row was created.
data[].promotional_banner_brands[].updated_atstring | nullNoNot providedISO 8601 timestamp at which the target row was last updated.

POST/promotional_banners/restaurant/{restaurantId}Create a local banner

Requires Bearer authentication and an Admin, Brand, or Restaurant role. The JSON body is required. Unknown fields are removed by Joi. The defaults are isActive=false, linkType=none, linkTargetId=null, channelId=1, and placement=restaurants.

Parameters

NameTypeRequiredDescription
restaurantIdintegerYesRestaurant that will own the banner (path parameter).

Request Body

json
{
  "name": "Lunch special",
  "imageId": 501,
  "isActive": true,
  "linkType": "product",
  "linkTargetId": 5001,
  "channelId": 1,
  "placement": "restaurants"
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
namestringYes"Lunch special"Banner name, with a maximum length of 255 characters.
imageIdintegerYes501Identifier of an uploaded image to display.
isActivebooleanNotrueInitial activation state. Defaults to false.
linkTypestringNo"product"Click behavior: `none`, `category`, or `product`. Defaults to `none`.
linkTargetIdinteger | nullNo5001Required when linkType is `category` or `product`; omitted or null when linkType is `none` (the default is null).
channelIdintegerNo1Display channel: `1` KIOSK or `2` WEB/CEL. Defaults to `1`.
placementstringNo"restaurants"Placement: `restaurants` or `foodcourt_home`. Defaults to `restaurants`.

Response

json
{
  "status": 201,
  "code": "create_promotional_banner_succeed",
  "message": "Promotional banner created successfully",
  "data": {
    "promotionalBannerId": 901,
    "name": "Lunch special",
    "imageId": 501,
    "isActive": true,
    "linkType": "product",
    "linkTargetId": 5001,
    "linkTargetRef": null,
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": 42,
    "brandId": null,
    "created_at": "2026-07-11T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 501,
      "fullUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes201HTTP status code, repeated in the successful JSON envelope.
codestringYes"create_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Promotional banner created successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Created local banner.
data.promotionalBannerIdintegerYes901Unique identifier of the promotional banner.
data.namestringYes"Lunch special"Human-readable banner name.
data.imageIdintegerYes501Identifier of the image selected for the banner.
data.isActivebooleanYestrueBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYes5001Local category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYesnullBrand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYes42Owning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYesnullOwning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-11T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo501Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the resolved banner image.

PUT/promotional_banners/{promotionalBannerId}Update a local banner

Requires Bearer authentication and an Admin, Brand, or Restaurant role. A JSON object is required but every documented member is optional. This endpoint only accepts local banners; attempting to update a brand banner is denied.

Parameters

NameTypeRequiredDescription
promotionalBannerIdintegerYesLocal banner identifier (path parameter).

Request Body

json
{
  "name": "Lunch special extended",
  "imageId": 503,
  "linkType": "category",
  "linkTargetId": 210
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
namestringNo"Lunch special extended"Replacement name, with a maximum length of 255 characters.
imageIdintegerNo503Replacement image identifier.
linkTypestringNo"category"Replacement click behavior: `none`, `category`, or `product`.
linkTargetIdinteger | nullNo210Required if this body sets linkType to `category` or `product`. Otherwise optional and may be null; the service rejects null for an effective non-`none` link.

Response

json
{
  "status": 200,
  "code": "update_promotional_banner_succeed",
  "message": "Promotional banner updated successfully",
  "data": {
    "promotionalBannerId": 901,
    "name": "Lunch special extended",
    "imageId": 503,
    "isActive": true,
    "linkType": "category",
    "linkTargetId": 210,
    "linkTargetRef": null,
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": 42,
    "brandId": null,
    "created_at": "2026-07-11T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 501,
      "fullUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"update_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Promotional banner updated successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Updated local banner.
data.promotionalBannerIdintegerYes901Unique identifier of the promotional banner.
data.namestringYes"Lunch special extended"Human-readable banner name.
data.imageIdintegerYes503Identifier of the image selected for the banner.
data.isActivebooleanYestrueBanner-level activation state.
data.linkTypestringYes"category"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYes210Local category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYesnullBrand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYes42Owning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYesnullOwning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-11T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo501Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the resolved banner image.

PATCH/promotional_banners/{promotionalBannerId}/toggleToggle a local banner

Requires Bearer authentication and an Admin, Brand, or Restaurant role. This endpoint only toggles a local banner, not a brand banner.

Parameters

NameTypeRequiredDescription
promotionalBannerIdintegerYesLocal banner identifier (path parameter).

Request Body

json
{
  "isActive": false
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
isActivebooleanYesfalseRequested local-banner activation state.

Response

json
{
  "status": 200,
  "code": "toggle_promotional_banner_succeed",
  "message": "Promotional banner toggled successfully",
  "data": {
    "promotionalBannerId": 901,
    "name": "Lunch special",
    "imageId": 501,
    "isActive": false,
    "linkType": "product",
    "linkTargetId": 5001,
    "linkTargetRef": null,
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": 42,
    "brandId": null,
    "created_at": "2026-07-11T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 501,
      "fullUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"toggle_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Promotional banner toggled successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Local banner after toggling.
data.promotionalBannerIdintegerYes901Unique identifier of the promotional banner.
data.namestringYes"Lunch special"Human-readable banner name.
data.imageIdintegerYes501Identifier of the image selected for the banner.
data.isActivebooleanYesfalseBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYes5001Local category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYesnullBrand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYes42Owning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYesnullOwning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-11T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo501Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the resolved banner image.

PATCH/promotional_banners/{promotionalBannerId}/restaurant/{restaurantId}/toggleToggle a brand banner for one restaurant

Requires Bearer authentication and an Admin, Brand, or Restaurant role. The banner must be brand-scoped and already targeted to the requested restaurant. The returned record is the global brand banner; it does not include the per-restaurant junction-row activation just changed.

Parameters

NameTypeRequiredDescription
promotionalBannerIdintegerYesBrand banner identifier (path parameter).
restaurantIdintegerYesTarget restaurant identifier (path parameter).

Request Body

json
{
  "isActive": false
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
isActivebooleanYesfalseRequested activation state for this restaurant target.

Response

json
{
  "status": 200,
  "code": "toggle_restaurant_brand_banner_succeed",
  "message": "Restaurant brand banner toggled successfully",
  "data": {
    "promotionalBannerId": 902,
    "name": "Brand summer offer",
    "imageId": 502,
    "isActive": true,
    "linkType": "product",
    "linkTargetId": null,
    "linkTargetRef": "SUMMER-BURGER",
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": null,
    "brandId": 7,
    "created_at": "2026-07-10T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 502,
      "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"toggle_restaurant_brand_banner_succeed"Stable application success code for this operation.
messagestringYes"Restaurant brand banner toggled successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Global brand banner record after the target-row update.
data.promotionalBannerIdintegerYes902Unique identifier of the promotional banner.
data.namestringYes"Brand summer offer"Human-readable banner name.
data.imageIdintegerYes502Identifier of the image selected for the banner.
data.isActivebooleanYestrueBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYesnullLocal category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYes"SUMMER-BURGER"Brand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYesnullOwning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYes7Owning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo502Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/brand-summer.jpg"Absolute URL of the resolved banner image.

POST/promotional_banners/{promotionalBannerId}/duplicateDuplicate a banner into a restaurant

Requires Bearer authentication and an Admin, Brand, or Restaurant role. Creates an inactive local copy in the supplied restaurant. Food-court-home banners cannot be duplicated. A brand product or category link is resolved against the target restaurant’s active menu and can become `none` if no equivalent target exists.

Parameters

NameTypeRequiredDescription
promotionalBannerIdintegerYesSource banner identifier (path parameter).

Request Body

json
{
  "restaurantId": 42
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
restaurantIdintegerYes42Restaurant that will own the newly created local copy.

Response

json
{
  "status": 201,
  "code": "duplicate_promotional_banner_succeed",
  "message": "Promotional banner duplicated",
  "data": {
    "promotionalBannerId": 903,
    "name": "Copie de Brand summer offer",
    "imageId": 501,
    "isActive": false,
    "linkType": "product",
    "linkTargetId": 5001,
    "linkTargetRef": null,
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": 42,
    "brandId": null,
    "created_at": "2026-07-11T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 501,
      "fullUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes201HTTP status code, repeated in the successful JSON envelope.
codestringYes"duplicate_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Promotional banner duplicated"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}New inactive local-banner copy.
data.promotionalBannerIdintegerYes903Unique identifier of the promotional banner.
data.namestringYes"Copie de Brand summer offer"Human-readable banner name.
data.imageIdintegerYes501Identifier of the image selected for the banner.
data.isActivebooleanYesfalseBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYes5001Local category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYesnullBrand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYes42Owning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYesnullOwning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-11T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo501Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the resolved banner image.

DELETE/promotional_banners/{promotionalBannerId}Delete a local banner

Requires Bearer authentication and an Admin, Brand, or Restaurant role. This endpoint soft-deletes a local banner. It returns HTTP 204, so Express sends no JSON envelope or response body.

Parameters

NameTypeRequiredDescription
promotionalBannerIdintegerYesLocal banner identifier (path parameter).

Response

json
HTTP 204 No Content

The server deliberately returns no response body.

Active display feeds

These guest-accessible endpoints return only the banner fields needed by a display client. They do not return the management representation above.

GET/promotional_banners/restaurant/{restaurantId}/activeGet active banners for a restaurant

Guest-accessible: no bearer token is required. Returns active local banners and active brand banners targeted to the restaurant for the requested channel. Product-linked banners whose products are unavailable on that channel are omitted. channelId defaults to 1 (KIOSK).

Parameters

NameTypeRequiredDescription
restaurantIdintegerYesRestaurant identifier (path parameter).
channelIdintegerNoOptional query parameter: `1` for KIOSK or `2` for WEB/CEL. Defaults to `1`.

Response

json
{
  "status": 200,
  "code": "list_promotional_banners_succeed",
  "message": "Promotional banners fetched successfully",
  "data": {
    "banners": [
      {
        "promotionalBannerId": 901,
        "imageUrl": "https://cdn.example.com/promotional-banners/lunch-special.jpg",
        "linkType": "product",
        "linkTargetId": 5001,
        "placement": "restaurants"
      }
    ],
    "carouselDelayMs": 4000
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_promotional_banners_succeed"Stable application success code for this operation.
messagestringYes"Promotional banners fetched successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Active display feed for the requested restaurant and channel.
data.bannersarrayYes[…]Active, channel-eligible banners to display in carousel order.
data.banners[]objectYes{…}One active banner display item.
data.banners[].promotionalBannerIdintegerYes901Unique identifier of the active banner.
data.banners[].imageUrlstring | nullYes"https://cdn.example.com/promotional-banners/lunch-special.jpg"Absolute URL of the banner image, or null when the image cannot be resolved.
data.banners[].linkTypestringYes"product"Resolved click behavior: `none`, `category`, or `product`.
data.banners[].linkTargetIdinteger | nullYes5001Resolved category or product ID in this restaurant’s active menu, or null when the banner is not linked.
data.banners[].placementstringYes"restaurants"Stored placement value: `restaurants` or `foodcourt_home`.
data.carouselDelayMsintegerYes4000Carousel delay in milliseconds. Defaults to 4000 when no brand value is configured.

GET/promotional_banners/brand/{brandId}/active/foodcourtGet active food-court banners for a brand

Guest-accessible: no bearer token is required. Returns only active WEB (CEL) brand banners whose placement is foodcourt_home. Product targets that no longer belong to the brand or are not available on WEB are omitted.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Response

json
{
  "status": 200,
  "code": "list_promotional_banners_succeed",
  "message": "Promotional banners fetched successfully",
  "data": {
    "banners": [
      {
        "promotionalBannerId": 904,
        "imageUrl": "https://cdn.example.com/promotional-banners/foodcourt.jpg",
        "linkType": "product",
        "linkTargetId": 5001,
        "linkTargetRef": "5001",
        "linkTargetRestaurantId": 42,
        "placement": "foodcourt_home"
      }
    ],
    "carouselDelayMs": 5000
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_promotional_banners_succeed"Stable application success code for this operation.
messagestringYes"Promotional banners fetched successfully"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Active food-court display feed for the brand.
data.bannersarrayYes[…]Active food-court-home banners to display.
data.banners[]objectYes{…}One active food-court banner display item.
data.banners[].promotionalBannerIdintegerYes904Unique identifier of the active food-court banner.
data.banners[].imageUrlstring | nullYes"https://cdn.example.com/promotional-banners/foodcourt.jpg"Absolute URL of the banner image, or null when the image cannot be resolved.
data.banners[].linkTypestringYes"product"Stored click behavior. New food-court banners may use `none` or `product`; a legacy `category` value is returned unchanged.
data.banners[].linkTargetIdinteger | nullYes5001Resolved product ID, or null when the banner has no product link.
data.banners[].linkTargetRefstring | nullYes"5001"Stored brand-level product reference, normally a decimal product-ID string for food-court banners.
data.banners[].linkTargetRestaurantIdinteger | nullYes42Restaurant that owns the resolved linked product, or null when there is no product link.
data.banners[].placementstringYes"foodcourt_home"Always `foodcourt_home` in this endpoint’s results.
data.carouselDelayMsintegerYes5000Carousel delay in milliseconds. Defaults to 4000 when no brand value is configured.

Brand banner management

Brand banners are created once and can be targeted to multiple restaurants. Brand product links use a SKU so the same banner can resolve to a product in each restaurant’s menu; category links use a category name. Food-court-home banners have no restaurant targets.

GET/brands/{brandId}/promotional_bannersList brand banners

Requires Bearer authentication and an Admin or Brand role. Returns each brand banner with its target rows and aggregate active/total target counts.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).
sortstringNoOptional query parameter. Use `name`, `status`, `linkType`, or `created_at`, as `field:asc`, `field:desc`, `-field`, or a comma-separated combination. Unknown fields are ignored; default order is created_at ascending.

Response

json
{
  "status": 200,
  "code": "list_brand_promotional_banners_succeed",
  "message": "Brand promotional banners fetched",
  "data": [
    {
      "promotionalBannerId": 902,
      "name": "Brand summer offer",
      "imageId": 502,
      "isActive": true,
      "linkType": "product",
      "linkTargetId": null,
      "linkTargetRef": "SUMMER-BURGER",
      "channelId": 1,
      "placement": "restaurants",
      "restaurantId": null,
      "brandId": 7,
      "created_at": "2026-07-10T09:00:00.000Z",
      "updated_at": "2026-07-11T09:15:00.000Z",
      "deleted_at": null,
      "image": {
        "imageId": 502,
        "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
      },
      "promotional_banner_brands": [
        {
          "promotionalBannerId": 902,
          "restaurantId": 42,
          "isActive": true,
          "created_at": "2026-07-10T09:00:00.000Z",
          "updated_at": "2026-07-11T09:15:00.000Z"
        },
        {
          "promotionalBannerId": 902,
          "restaurantId": 43,
          "isActive": false,
          "created_at": "2026-07-10T09:00:00.000Z",
          "updated_at": "2026-07-11T09:15:00.000Z"
        }
      ],
      "activationCount": 1,
      "targetCount": 2
    }
  ]
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_brand_promotional_banners_succeed"Stable application success code for this operation.
messagestringYes"Brand promotional banners fetched"Localized human-readable success message. Do not use this value for program logic.
dataarrayYes[…]Brand banner records including target rows and counts.
data[]objectNot specified{…}Endpoint-specific response payload.
data[].promotionalBannerIdintegerYes902Unique identifier of the promotional banner.
data[].namestringYes"Brand summer offer"Human-readable banner name.
data[].imageIdintegerYes502Identifier of the image selected for the banner.
data[].isActivebooleanYestrueBanner-level activation state.
data[].linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data[].linkTargetIdinteger | nullYesnullLocal category/product target ID when known. Brand banners normally use linkTargetRef instead.
data[].linkTargetRefstring | nullYes"SUMMER-BURGER"Brand-level target reference (SKU for products or category name for categories), or null when unused.
data[].channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data[].placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data[].restaurantIdinteger | nullYesnullOwning restaurant for a local banner; null for a brand-scoped banner.
data[].brandIdinteger | nullYes7Owning brand for a brand-scoped banner; null for a local banner.
data[].created_atstringYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data[].updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data[].deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data[].imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data[].image.imageIdintegerNo502Identifier of the resolved image.
data[].image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/brand-summer.jpg"Absolute URL of the resolved banner image.
data[].promotional_banner_brandsarrayYes[…]Per-restaurant target rows. This property is present for brand banners in list responses, not local banners.
data[].promotional_banner_brands[]objectYes{…}One brand-banner assignment to a restaurant.
data[].promotional_banner_brands[].promotionalBannerIdintegerYes902Identifier of the brand banner assigned to the restaurant.
data[].promotional_banner_brands[].restaurantIdintegerYes42Identifier of the target restaurant.
data[].promotional_banner_brands[].isActivebooleanYestrueActivation state for this target restaurant.
data[].promotional_banner_brands[].created_atstring | nullYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the target row was created.
data[].promotional_banner_brands[].updated_atstring | nullYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the target row was last updated.
data[].activationCountintegerYes1Number of target restaurants where this banner is currently active.
data[].targetCountintegerYes2Number of target restaurants attached to this banner, active or inactive.

POST/brands/{brandId}/promotional_bannersCreate a brand banner

Requires Bearer authentication and an Admin or Brand role. The JSON body is required and unknown fields are removed by Joi. For placement=restaurants, an active banner needs at least one eligible target restaurant owned by the brand. For placement=foodcourt_home, restaurantIds is ignored and linkType=category is rejected by the service.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand that will own the banner (path parameter).

Request Body

json
{
  "name": "Brand summer offer",
  "imageId": 502,
  "isActive": true,
  "linkType": "product",
  "linkTargetRef": "SUMMER-BURGER",
  "channelId": 1,
  "placement": "restaurants",
  "restaurantIds": [
    42,
    43
  ]
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
namestringYes"Brand summer offer"Banner name, with a maximum length of 255 characters.
imageIdintegerYes502Identifier of an uploaded image to display.
isActivebooleanNotrueInitial global activation state. Defaults to false.
linkTypestringNo"product"Click behavior: `none`, `category`, or `product`. Defaults to `none`.
linkTargetRefstring | nullNo"SUMMER-BURGER"Required when linkType is `category` or `product`; otherwise optional, nullable, and defaults to null. Use a category name for category links and a SKU for restaurant-placement product links. A food-court product link must use a decimal product-ID string.
channelIdintegerNo1Display channel: `1` KIOSK or `2` WEB/CEL. Defaults to `1`.
placementstringNo"restaurants"Placement: `restaurants` or `foodcourt_home`. Defaults to `restaurants`. Only WEB food-court banners are returned by the public food-court feed.
restaurantIdsarray | nullNo[…]Target restaurant IDs. Defaults to an empty array and is ignored for foodcourt_home placement.
restaurantIds[]integerNo42One target restaurant identifier, which must belong to the brand.

Response

json
{
  "status": 201,
  "code": "create_brand_promotional_banner_succeed",
  "message": "Brand promotional banner created",
  "data": {
    "promotionalBannerId": 902,
    "name": "Brand summer offer",
    "imageId": 502,
    "isActive": true,
    "linkType": "product",
    "linkTargetId": null,
    "linkTargetRef": "SUMMER-BURGER",
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": null,
    "brandId": 7,
    "created_at": "2026-07-10T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 502,
      "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes201HTTP status code, repeated in the successful JSON envelope.
codestringYes"create_brand_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Brand promotional banner created"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Created brand banner. Target rows are not included in this response.
data.promotionalBannerIdintegerYes902Unique identifier of the promotional banner.
data.namestringYes"Brand summer offer"Human-readable banner name.
data.imageIdintegerYes502Identifier of the image selected for the banner.
data.isActivebooleanYestrueBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYesnullLocal category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYes"SUMMER-BURGER"Brand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYesnullOwning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYes7Owning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo502Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/brand-summer.jpg"Absolute URL of the resolved banner image.

PUT/brands/{brandId}/promotional_banners/{promotionalBannerId}Update a brand banner

Requires Bearer authentication and an Admin or Brand role. A JSON object is required, but all its documented members are optional. `restaurantIdsToAdd` and `restaurantIdsToRemove` default to empty arrays. When placement becomes foodcourt_home, target changes are ignored and existing restaurant targets are detached; foodcourt_home cannot use linkType=category.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier in the route (path parameter). Access is checked against the banner owner.
promotionalBannerIdintegerYesBrand banner identifier (path parameter).

Request Body

json
{
  "name": "Brand summer offer extended",
  "imageId": 504,
  "linkType": "product",
  "linkTargetRef": "SUMMER-BURGER-XL",
  "placement": "restaurants",
  "restaurantIdsToAdd": [
    44
  ],
  "restaurantIdsToRemove": [
    43
  ]
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
namestringNo"Brand summer offer extended"Replacement name, with a maximum length of 255 characters.
imageIdintegerNo504Replacement image identifier.
linkTypestringNo"product"Replacement click behavior: `none`, `category`, or `product`. `category` is invalid with foodcourt_home.
linkTargetRefstring | nullNo"SUMMER-BURGER-XL"Replacement reusable target reference, up to 255 characters; null is accepted. The schema does not make this field conditionally required for a partial update.
placementstringNo"restaurants"Replacement placement: `restaurants` or `foodcourt_home`.
restaurantIdsToAddarrayNo[…]Restaurant target IDs to attach. Defaults to []; ignored for foodcourt_home. Each added restaurant must belong to the brand.
restaurantIdsToAdd[]integerNo44One restaurant target to attach.
restaurantIdsToRemovearrayNo[…]Restaurant target IDs to detach. Defaults to []; ignored for foodcourt_home.
restaurantIdsToRemove[]integerNo43One restaurant target to detach.

Response

json
{
  "status": 200,
  "code": "update_brand_promotional_banner_succeed",
  "message": "Brand promotional banner updated",
  "data": {
    "promotionalBannerId": 902,
    "name": "Brand summer offer extended",
    "imageId": 504,
    "isActive": true,
    "linkType": "product",
    "linkTargetId": null,
    "linkTargetRef": "SUMMER-BURGER-XL",
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": null,
    "brandId": 7,
    "created_at": "2026-07-10T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 502,
      "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"update_brand_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Brand promotional banner updated"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Updated brand banner. Target rows are not included in this response.
data.promotionalBannerIdintegerYes902Unique identifier of the promotional banner.
data.namestringYes"Brand summer offer extended"Human-readable banner name.
data.imageIdintegerYes504Identifier of the image selected for the banner.
data.isActivebooleanYestrueBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYesnullLocal category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYes"SUMMER-BURGER-XL"Brand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYesnullOwning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYes7Owning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo502Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/brand-summer.jpg"Absolute URL of the resolved banner image.

PATCH/brands/{brandId}/promotional_banners/{promotionalBannerId}/toggleToggle a brand banner globally

Requires Bearer authentication and an Admin or Brand role. For restaurants placement, toggling also sets the same activation state on every target row. Activating a restaurants-placement banner with no targets is rejected. Food-court-home banners have no target rows and use only their global isActive state.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier in the route (path parameter). Access is checked against the banner owner.
promotionalBannerIdintegerYesBrand banner identifier (path parameter).

Request Body

json
{
  "isActive": false
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
isActivebooleanYesfalseRequested global activation state.

Response

json
{
  "status": 200,
  "code": "toggle_brand_promotional_banner_succeed",
  "message": "Brand promotional banner toggled",
  "data": {
    "promotionalBannerId": 902,
    "name": "Brand summer offer",
    "imageId": 502,
    "isActive": false,
    "linkType": "product",
    "linkTargetId": null,
    "linkTargetRef": "SUMMER-BURGER",
    "channelId": 1,
    "placement": "restaurants",
    "restaurantId": null,
    "brandId": 7,
    "created_at": "2026-07-10T09:00:00.000Z",
    "updated_at": "2026-07-11T09:15:00.000Z",
    "deleted_at": null,
    "image": {
      "imageId": 502,
      "fullUrl": "https://cdn.example.com/promotional-banners/brand-summer.jpg"
    }
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"toggle_brand_promotional_banner_succeed"Stable application success code for this operation.
messagestringYes"Brand promotional banner toggled"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Brand banner after global toggle.
data.promotionalBannerIdintegerYes902Unique identifier of the promotional banner.
data.namestringYes"Brand summer offer"Human-readable banner name.
data.imageIdintegerYes502Identifier of the image selected for the banner.
data.isActivebooleanYesfalseBanner-level activation state.
data.linkTypestringYes"product"Link behavior: `none`, `category`, or `product`.
data.linkTargetIdinteger | nullYesnullLocal category/product target ID when known. Brand banners normally use linkTargetRef instead.
data.linkTargetRefstring | nullYes"SUMMER-BURGER"Brand-level target reference (SKU for products or category name for categories), or null when unused.
data.channelIdintegerYes1Display channel: `1` for KIOSK and `2` for WEB (CEL).
data.placementstringYes"restaurants"Placement: `restaurants` or `foodcourt_home`.
data.restaurantIdinteger | nullYesnullOwning restaurant for a local banner; null for a brand-scoped banner.
data.brandIdinteger | nullYes7Owning brand for a brand-scoped banner; null for a local banner.
data.created_atstringYes"2026-07-10T09:00:00.000Z"ISO 8601 timestamp at which the banner was created.
data.updated_atstringYes"2026-07-11T09:15:00.000Z"ISO 8601 timestamp at which the banner was last updated.
data.deleted_atstring | nullYesnullSoft-deletion timestamp. It is null for records returned by these endpoints.
data.imageobject | nullYes{…}Resolved image record, when the referenced image still exists.
data.image.imageIdintegerNo502Identifier of the resolved image.
data.image.fullUrlstring | nullNo"https://cdn.example.com/promotional-banners/brand-summer.jpg"Absolute URL of the resolved banner image.

DELETE/brands/{brandId}/promotional_banners/{promotionalBannerId}Delete a brand banner

Requires Bearer authentication and an Admin or Brand role. Deletes the banner and all of its restaurant target rows. It returns HTTP 204, so Express sends no JSON envelope or response body.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier in the route (path parameter). Access is checked against the banner owner.
promotionalBannerIdintegerYesBrand banner identifier (path parameter).

Response

json
HTTP 204 No Content

The server deliberately returns no response body.

Brand settings and selection data

These authenticated brand endpoints support carousel configuration and user interfaces that choose banner targets. The pickup-schedule endpoint lives with the promotional-banner controllers because the food-court configuration uses the brand’s restaurant pickup availability.

GET/brands/{brandId}/promotional_banners_settingsGet brand carousel settings

Requires Bearer authentication and an Admin or Brand role. Returns the shared carousel delay used by both KIOSK and WEB banner feeds. It defaults to 4000 milliseconds when the brand has no configured value.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Response

json
{
  "status": 200,
  "code": "get_brand_promotional_banners_settings_succeed",
  "message": "Settings fetched",
  "data": {
    "carouselDelayMs": 4000
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"get_brand_promotional_banners_settings_succeed"Stable application success code for this operation.
messagestringYes"Settings fetched"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Brand carousel configuration.
data.carouselDelayMsintegerYes4000Carousel delay in milliseconds; 4000 is the service default.

PUT/brands/{brandId}/promotional_banners_settingsUpdate brand carousel settings

Requires Bearer authentication and an Admin or Brand role. The JSON body is required. Changing the delay reloads the brand’s restaurants; sending the existing value leaves it unchanged.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Request Body

json
{
  "carouselDelayMs": 5000
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
carouselDelayMsintegerYes5000Carousel delay in milliseconds. Must be an integer greater than or equal to 1.

Response

json
{
  "status": 200,
  "code": "update_brand_promotional_banners_settings_succeed",
  "message": "Settings updated",
  "data": {
    "carouselDelayMs": 5000
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"update_brand_promotional_banners_settings_succeed"Stable application success code for this operation.
messagestringYes"Settings updated"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Updated brand carousel configuration.
data.carouselDelayMsintegerYes5000Persisted carousel delay in milliseconds.

GET/brands/{brandId}/promotional_banner_targetsList eligible brand-banner restaurants

Requires Bearer authentication and an Admin or Brand role. Returns every restaurant owned by the brand and independently reports whether it has each display channel and an active menu on that channel.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Response

json
{
  "status": 200,
  "code": "list_brand_promotional_banner_targets_succeed",
  "message": "Brand promotional banner targets fetched",
  "data": [
    {
      "restaurantId": 42,
      "name": "Central restaurant",
      "hasKioskChannel": true,
      "hasActiveKioskMenu": true,
      "hasCelChannel": true,
      "hasActiveCelMenu": false
    }
  ]
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_brand_promotional_banner_targets_succeed"Stable application success code for this operation.
messagestringYes"Brand promotional banner targets fetched"Localized human-readable success message. Do not use this value for program logic.
dataarrayYes[…]Restaurants available as brand-banner targets.
data[]objectYes{…}One restaurant owned by the brand.
data[].restaurantIdintegerYes42Restaurant identifier.
data[].namestringYes"Central restaurant"Restaurant name; it can be an empty string when no name is stored.
data[].hasKioskChannelbooleanYestrueWhether the restaurant has the KIOSK channel configured.
data[].hasActiveKioskMenubooleanYestrueWhether the restaurant has an active KIOSK menu.
data[].hasCelChannelbooleanYestrueWhether the restaurant has the WEB/CEL channel configured.
data[].hasActiveCelMenubooleanYesfalseWhether the restaurant has an active WEB/CEL menu.

GET/brands/{brandId}/promotional_banner_targets/cel_productsList CEL products across a brand

Requires Bearer authentication and an Admin or Brand role. Aggregates products from every active WEB/CEL menu of the brand’s restaurants. This is the product picker for food-court-home banner links.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Response

json
{
  "status": 200,
  "code": "list_brand_promotional_banner_cel_products_succeed",
  "message": "Brand CEL products fetched",
  "data": [
    {
      "restaurantId": 42,
      "restaurantName": "Central restaurant",
      "productId": 5001,
      "productName": "Classic burger",
      "sku": "SUMMER-BURGER"
    }
  ]
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_brand_promotional_banner_cel_products_succeed"Stable application success code for this operation.
messagestringYes"Brand CEL products fetched"Localized human-readable success message. Do not use this value for program logic.
dataarrayYes[…]Products from active brand WEB/CEL menus.
data[]objectYes{…}One product available in an active WEB/CEL menu.
data[].restaurantIdintegerYes42Restaurant that owns the active menu containing this product.
data[].restaurantNamestringYes"Central restaurant"Name of that restaurant; it can be an empty string when no name is stored.
data[].productIdintegerYes5001Product identifier.
data[].productNamestringYes"Classic burger"Product display name.
data[].skustring | nullYes"SUMMER-BURGER"Product SKU, or null when the product has no SKU.

GET/brands/{brandId}/pickup_schedulesList brand restaurant pickup schedules

Requires Bearer authentication and an Admin or Brand role. Returns all pickup schedules grouped by restaurant ID. Every restaurant owned by the brand is present as a JSON object key, including those with no schedules.

Parameters

NameTypeRequiredDescription
brandIdintegerYesBrand identifier (path parameter).

Response

json
{
  "status": 200,
  "code": "list_brand_pickup_schedules_succeed",
  "message": "Brand pickup schedules fetched",
  "data": {
    "42": [
      {
        "pickupScheduleId": 801,
        "restaurantId": 42,
        "day": "MON",
        "start": 660,
        "end": 840
      }
    ],
    "43": []
  }
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes200HTTP status code, repeated in the successful JSON envelope.
codestringYes"list_brand_pickup_schedules_succeed"Stable application success code for this operation.
messagestringYes"Brand pickup schedules fetched"Localized human-readable success message. Do not use this value for program logic.
dataobjectYes{…}Object keyed by restaurant ID (JSON object keys are strings).
data.42arrayYes[…]Example schedule array for restaurant 42. In production, replace `42` with any restaurant ID owned by the requested brand.
data.42[]objectYes{…}One pickup schedule for the restaurant represented by the object key.
data.42[].pickupScheduleIdintegerYes801Pickup schedule identifier.
data.42[].restaurantIdintegerYes42Restaurant identifier; it matches the enclosing object key.
data.42[].daystringYes"MON"Weekday code: MON, TUE, WED, THU, FRI, SAT, or SUN.
data.42[].startintegerYes660Start minute after midnight (0–1440). Example 660 is 11:00.
data.42[].endintegerYes840End minute after midnight (0–1440). Example 840 is 14:00.
data.43arrayYes[]Example empty schedule array for a brand restaurant with no pickup schedules.

Engagement tracking

The tracking endpoint accepts interaction events from CEL and Borne. Events are best-effort: once validation succeeds, a Pub/Sub publish failure is logged internally and does not turn the request into an error response.

POST/tracking/promotional_banner_eventTrack a promotional-banner event

Guest-accessible: no bearer token is required. The JSON body is required and unknown fields are stripped by Joi. clientTimestamp must be an ISO date string. productId is required only for product_added_post_banner and is optional for promotional_banner_clicked.

Request Body

json
{
  "eventName": "product_added_post_banner",
  "bannerId": 904,
  "restaurantId": 42,
  "canal": "CEL",
  "clientTimestamp": "2026-07-11T10:30:00.000Z",
  "productId": 5001
}
Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
eventNamestringYes"product_added_post_banner"Event type: `promotional_banner_clicked` or `product_added_post_banner`.
bannerIdintegerYes904Identifier of the banner that led to the interaction.
restaurantIdintegerYes42Restaurant context in which the event occurred.
canalstringYes"CEL"Client channel: `CEL` for web ordering or `BORNE` for kiosk.
clientTimestampstringYes"2026-07-11T10:30:00.000Z"ISO 8601 timestamp generated by the client.
productIdintegerNo5001Required when eventName is `product_added_post_banner`; optional for `promotional_banner_clicked`.

Response

json
{
  "status": 202,
  "code": "track_promotional_banner_event_succeed",
  "message": "Promotional banner event tracked",
  "data": null
}
Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

PropertyTypeRequiredExampleDescription
statusintegerYes202HTTP status code, repeated in the successful JSON envelope.
codestringYes"track_promotional_banner_event_succeed"Stable application success code for this operation.
messagestringYes"Promotional banner event tracked"Localized human-readable success message. Do not use this value for program logic.
datanullYesnullAlways null. The event has been accepted for best-effort asynchronous publishing.