Nexus Gateway
EnglishRomână
OpenAPI

The published menu is the source of truth for your cart. The gateway checks every product, choice and total against the menu revision you send.

GET /v1/connections/{connection_id}/menu returns everything a customer can order at that location: categories, products with their choices, the enabled fulfillment modes, the delivery fee and a revision.

  • IDs are opaque strings. Store and send them as they are; don’t parse them.
  • Names and descriptions are restaurant-entered text. Render them as text, never as HTML.
View the complete sample menu Pizza + burger meal
menu.json
{
  "revision": "example-menu-1",
  "currency": "RON",
  "accepting_orders": true,
  "fulfillment": [
    "pickup",
    "delivery"
  ],
  "delivery_fee": 700,
  "categories": [
    {
      "id": "pizzas",
      "name": "Pizza"
    },
    {
      "id": "meals",
      "name": "Meals"
    }
  ],
  "products": [
    {
      "id": "pizza-margherita",
      "category_id": "pizzas",
      "name": "Margherita",
      "description": "Tomato, mozzarella and basil",
      "image_url": "https://images.example.test/margherita.jpg",
      "translations": {
        "en": {
          "name": "Margherita",
          "description": "Tomato, mozzarella and basil"
        },
        "ro": {
          "name": "Margherita",
          "description": "Roșii, mozzarella și busuioc"
        }
      },
      "price": 3200,
      "packaging": 100,
      "packaging_product_id": "pizza-box",
      "modifier_groups": [
        {
          "id": "pizza-extras",
          "name": "Extras",
          "kind": "extra",
          "minimum": 0,
          "maximum": 1,
          "options": [
            {
              "id": "cheese",
              "name": "Extra cheese",
              "price": 400
            }
          ]
        }
      ]
    },
    {
      "id": "burger-meal",
      "category_id": "meals",
      "name": "Burger meal",
      "description": "Burger, fries and your choice of drink",
      "translations": {
        "en": {
          "name": "Burger meal",
          "description": "Burger, fries and your choice of drink"
        },
        "ro": {
          "name": "Meniu burger",
          "description": "Burger, cartofi prăjiți și băutură la alegere"
        }
      },
      "price": 2500,
      "packaging": 100,
      "packaging_product_id": "meal-bag",
      "modifier_groups": [
        {
          "id": "size",
          "name": "Size",
          "kind": "variant",
          "minimum": 1,
          "maximum": 1,
          "options": [
            {
              "id": "regular",
              "name": "Regular",
              "price": 0
            },
            {
              "id": "large",
              "name": "Large",
              "price": 0
            }
          ]
        },
        {
          "id": "drink",
          "name": "Drink",
          "kind": "bundle",
          "minimum": 1,
          "maximum": 1,
          "options": [
            {
              "id": "water",
              "name": "Water",
              "price": 0,
              "deposit": 50
            }
          ]
        },
        {
          "id": "meal-extras",
          "name": "Extras",
          "kind": "extra",
          "minimum": 0,
          "maximum": 1,
          "options": [
            {
              "id": "sauce",
              "name": "Extra sauce",
              "price": 200
            }
          ]
        }
      ],
      "variants": [
        {
          "id": "meal-regular",
          "selections": [
            {
              "group_id": "size",
              "option_id": "regular"
            }
          ],
          "price": 2500
        },
        {
          "id": "meal-large",
          "selections": [
            {
              "group_id": "size",
              "option_id": "large"
            }
          ],
          "price": 3000
        }
      ]
    }
  ]
}

Download sample menu ↓ · Menu schema →

Images and languages

Products can include an image_url, an absolute HTTP(S) URL, or null when no image is configured. Reserve space for images and show a fallback if loading fails. The gateway publishes the URL; it does not fetch or host the image.

Categories, products, modifier groups and options include translations, keyed by locale, such as ro, en and hu. Each entry contains name and description. Select the customer's locale, then its base language, then fall back to the item's existing name and description. Missing translations are not generated automatically. Render all translated content as text. Language selection never changes IDs, selections or prices.

Website product schedules are configured in Retail’s product editor, separately from Bolt, for each point of sale. Per-product promotions are not currently available in Retail’s Website integration; checkout-level discounts remain supported.

Opening hours, holidays and product schedules

opening_hours describes the connection's restaurant time zone, weekly periods and special_dates. Days run from Monday 1 to Sunday 7. Weekly intervals may cross midnight; the end is exclusive and 24:00 means the end of the day. When schedule_enabled is false, weekly hours do not restrict ordering.

Each special date replaces the whole local calendar day, including any overnight hours from the previous day. An empty periods list means closed. Date-specific intervals stay within that date; split an overnight exception into two dates. A date override also suppresses weekly spillover from that date into the following day. Special dates apply even when weekly hours are disabled. A manual pause still takes precedence.

A product's schedule uses weekly intervals in the same time zone: null means no product-specific restriction; [] means unavailable all week. Product hours can only restrict availability, never reopen a closed restaurant or restore stock. Use accepting_orders and available for the current decision; the gateway evaluates the rules again at checkout. These settings can change without a menu revision.

Handle connection.settings_changed to refresh opening hours, holiday exceptions, time zone and preparation time even while the restaurant remains open.

Keep the revision with the cart

Store revision with the cart and send it as menu_revision in the order. A new revision means prices or the catalog changed.

If you submit an outdated revision, the gateway returns 409 menu_changed. Fetch the menu, rebuild the cart and ask the customer to confirm any price change. A retry of an order you already submitted is different: it keeps its original prices, so resend it unchanged (see the retry protocol).

Register menu webhooks to refresh automatically after publication or product/option availability changes. Fetch the affected connection’s menu on menu.published or menu.availability_changed; the payload is an invalidation, not the catalog itself. Still recheck availability before checkout and after missed notifications.

Check availability before checkout

In Retail → Product availability, staff can switch products and options on or off separately for each connected Website point of sale, including bulk edits. Save changes queues an availability update automatically; another menu publication is not required. Local and other-platform switches remain independent. Stock and required-option rules still apply. A point of sale needs a published menu before its items can be edited here.

FieldWhat to do
accepting_ordersWhen false, disable checkout for the evaluated time. For scheduled fulfillment, fetch the menu with fulfillment_at for the chosen slot; closure now does not rule out a valid future slot.
fulfillmentBoth pickup and delivery are supported.
availableWhen false on a product, variant or option, show it as sold out and don’t let the customer select it.
minimum / maximumThe number of different options the customer must and may choose in a group.

These flags change without a new revision: availability follows the restaurant’s stock about once a minute, and pauses and opening hours apply immediately. Fetch the menu again just before checkout, and still handle a conflict if something sells out in between.

Calculate with integer bani

All amounts are integers in bani: 3700 means 37.00 RON. Calculate with integers and format decimals only for display.

unit = base price - published discount + base deposit + packaging
     + sum(extra prices + extra deposits)
     + sum(selected bundle deposits)

order total = sum(unit × quantity) - checkout_discount.amount + tip
            + (delivery_fee if fulfillment is delivery, otherwise 0)
  • For a product with variants, the matched variant’s price, discount and deposit replace the product’s.
  • Packaging is charged per unit. The restaurant adds the packaging item to the receipt; don’t add your own line for it.
  • The delivery fee is charged once per order, never for pickup.

Your total is checked, not trusted: the gateway recalculates it and returns 409 total_mismatch if they differ. Do not send line prices. Treat an omitted checkout_discount as zero. Website coupons use the checkout_discount contract described in the cart guide. The cart guide has a tested function that does this calculation for you.

How the menu gets published

The restaurant publishes its menu from Nexus Retail (Retail Admin → Integrations → POS → Website), with pickup and delivery both supported. You only read the published menu; there is nothing to call when it changes.

  • Each publication replaces the whole menu with a new revision.
  • The current menu stays available while a new one is being published.
  • Products the restaurant can’t sell at that location are published with available: false.
  • If GET returns 404 menu_not_published, ask the restaurant to publish.
Nexus GatewayWebsite Ordering API · v1.0.0

Type to search the integration guides.