☰ Menu · Menu
API reference

Menu

The venue's QR menu: categories, dishes, modifiers, prices and the stop list.

Dishes, categories and modifier values can carry your external_id (your SKU), unique per venue. Wherever a path has {item} or {value}, you can use our id or your external_id. Names and descriptions are maps by language ({"ro": "Zeamă", "en": "Chicken soup"}); add ?lang=en to any GET to get plain strings in one language, falling back to the menu's main language.

GET/v1/menu
Scope: menu:read

The whole menu as a tree: categories, their dishes, each dish's modifier groups and values. Hidden categories and dishes are left out unless you add ?include_hidden=true.

curl
curl "https://loyaltyfy.io/api/v1/menu?lang=en" \
  -H "Authorization: Bearer sk_test_..."
Response · 200
{
  "object": "menu",
  "url": "https://loyaltyfy.io/m/your-venue",
  "published": true,
  "currency": "MDL",
  "primary_language": "ro",
  "languages": ["ru", "en"],
  "categories": [
    {
      "object": "menu_category",
      "id": "0b8e4c1d-6f0e-4b8a-9a55-3c1d2e7f9a10",
      "external_id": "CAT-COFFEE",
      "parent_id": null,
      "name": "Coffee",
      "emoji": null,
      "visible": true,
      "items": [
        {
          "object": "menu_item",
          "id": "a48343bd-9f6e-424a-b003-fe790617f4d6",
          "external_id": "1001",
          "category_id": "0b8e4c1d-6f0e-4b8a-9a55-3c1d2e7f9a10",
          "name": "Flat white",
          "description": "Double ristretto, steamed milk",
          "price": 4500,
          "old_price": null,
          "currency": "MDL",
          "available": true,
          "visible": true,
          "photo_url": "https://...",
          "options": [
            {
              "object": "option_group",
              "id": "5c2f...",
              "kind": "addon",
              "name": "Milk",
              "min_select": 0,
              "max_select": 1,
              "values": [
                {
                  "object": "option_value",
                  "id": "9e41...",
                  "external_id": "MOD-OAT",
                  "name": "Oat milk",
                  "price_delta": 800,
                  "available": true,
                  "default": false
                }
              ]
            }
          ],
          "updated_at": "2026-10-06T09:12:44.120+00:00"
        }
      ]
    }
  ]
}
GET/v1/menu/items
Scope: menu:read

A flat list of dishes without modifiers, up to 1000, in menu order. Filters: ?external_id= for one SKU, ?available=false for the current stop list, ?lang=.

GET/v1/menu/items/{item}
Scope: menu:read

One dish with its modifiers.

POST/v1/menu/items
Scope: menu:write

Adds a dish. With an external_id the call is safe to repeat: an existing dish with that external_id is returned with 200.

categorystring, required
Category id or category external_id. An unknown value creates a category with that name.
nameobject, required
Name by language, at least one.
priceinteger, required
Minor units.
external_idstring
Your SKU.
descriptionobject
By language.
available, visibleboolean
Default true.
curl
curl https://loyaltyfy.io/api/v1/menu/items \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "category": "CAT-SOUPS",
    "external_id": "2040",
    "name": { "ro": "Zeamă", "ru": "Зама", "en": "Chicken soup" },
    "price": 6500
  }'
PATCH/v1/menu/items/{item}
Scope: menu:write

Changes price, old_price (shown crossed out), available, visible, external_id, name or description. Sends menu.updated.

curl
# the kitchen ran out: hide the "+" button in the menu
curl -X PATCH https://loyaltyfy.io/api/v1/menu/items/2040 \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "available": false }'
POST/v1/menu/items/bulk
Scope: menu:write

Up to 500 changes in one call. Each entry addresses a dish by item (id or external_id) and sets any of price, old_price, available, visible. Entries that change nothing come back in unchanged, unknown ones in not_found. One menu.updated event for the batch.

curl
curl https://loyaltyfy.io/api/v1/menu/items/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "item": "1001", "price": 4800 },
      { "item": "2040", "available": false },
      { "item": "3017", "price": 9900, "old_price": 12000 }
    ]
  }'
Response · 200
{
  "object": "menu_bulk_result",
  "updated": ["1001", "2040"],
  "unchanged": [],
  "not_found": ["3017"],
  "failed": []
}
PATCH/v1/menu/option_values/{value}
Scope: menu:write

Changes a modifier value: available, price_delta, external_id.

curl
curl -X PATCH https://loyaltyfy.io/api/v1/menu/option_values/MOD-OAT \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "available": false }'
Questions about an integration: api@loyaltyfy.io. We answer within one business day.