Developers · Menu & pricing

The whole menu — in one API.

Products, modifier groups, option groups, prices, allergens, deals. Read the whole catalogue, mutate what you need, let the platform propagate changes to every channel within seconds.

One price change here. Live on the website, the kiosk, the POS and every aggregator within seconds.

Products, modifiers, options, prices, allergens.

A product is a menu item. A modifier group attaches to it (sauces, sides, crust). An option group sits inside a modifier (extra cheese, no onions). Prices attach per variant, per occasion. Allergens attach per product. Deals compose products into offers.

Reads — Queries API

  • GET /organizations/{orgId}/productcatalogs
  • GET /organizations/{orgId}/products
  • GET /organizations/{orgId}/products/{id}
  • GET /organizations/{orgId}/modifiergroups
  • GET /organizations/{orgId}/optiongroups
  • GET /organizations/{orgId}/deals
  • GET /organizations/{orgId}/menus/locations/{locationId}

Writes — Commands API

  • POST /organizations/{orgId}/products
  • PUT /organizations/{orgId}/products/{id}/pricing
  • PUT /organizations/{orgId}/products/{id}/allergens
  • PUT /organizations/{orgId}/products/{id}/availability
  • POST /organizations/{orgId}/modifiergroups
  • POST /organizations/{orgId}/deals

Six things every menu integration needs.

1. Read the published menu at a location

Returns the full composed menu — products, variants, prices by occasion, modifiers, option groups, allergens and deals — as it will appear to a customer ordering at that location. Supports ETags for cheap polling.

GET
Request
GET /organizations/{organizationId}/menus/locations/{locationId}
Authorization: Bearer <access_token>
If-None-Match: "d41d8cd98f00b204e9800998ecf8427e"
Response — 200 OK (or 304 Not Modified)
{
  "locationId": "7f2b6c9e-1a4d-4f12-9b23-5c8e0d1a3b42",
  "etag": "3fa5c9b2e1d74d56b82a9f0ed1c4b711",
  "categories": [
    {
      "name": "Pizza",
      "products": [
        {
          "id": "a1c23f44-...",
          "name": "Margherita",
          "variants": [
            { "name": "Regular", "delivery": 9.50, "collection": 8.50 },
            { "name": "Large",   "delivery": 14.50, "collection": 13.50 }
          ],
          "allergens": ["Cereals", "Milk"]
        }
      ]
    }
  ]
}

2. Create a new product

One endpoint whether you're adding a main, a side, a dessert or a drink — the classification is done via productGroupId. The response returns a GUID; follow up with pricing, allergens and availability calls.

POST
Request
POST /organizations/{organizationId}/products
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "Garlic Bread",
  "description": "Fresh-baked flatbread, garlic butter, parsley",
  "productGroupId": "b5c7a0f4-3e2a-4c1d-8e11-2a9f4bd31c27",
  "sku": "SIDE-GARLIC-01"
}
Response — 202 Accepted
{
  "id": "8f1d2a74-9c3e-4a6b-b0ff-2e5c741a8d93",
  "status": "Queued"
}

3. Update prices across variants and occasions

Price per variant (Regular, Large), per occasion (Delivery, Collection, DineIn, Counter). Tax rate per price. One call replaces the entire pricing matrix for the product — no partial updates to reason about.

PUT
Request
PUT /organizations/{organizationId}/products/{productId}/pricing
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "variants": [
    {
      "name": "Regular",
      "prices": [
        { "occasion": "Delivery",   "price": 9.50, "taxRate": 0.20 },
        { "occasion": "Collection", "price": 8.50, "taxRate": 0.20 },
        { "occasion": "DineIn",     "price": 9.00, "taxRate": 0.20 }
      ]
    },
    {
      "name": "Large",
      "prices": [
        { "occasion": "Delivery",   "price": 14.50, "taxRate": 0.20 },
        { "occasion": "Collection", "price": 13.50, "taxRate": 0.20 }
      ]
    }
  ]
}

4. Declare allergens — Natasha's Law compliant

Every allergen declaration is either Contains or MayContain, with an optional free-text note. Allergen data flows through to the consumer website, kiosk, receipts and every aggregator menu export.

PUT
Request
PUT /organizations/{organizationId}/products/{productId}/allergens
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "allergens": [
    { "allergen": "Cereals", "assertion": "Contains",   "note": "Wheat flour" },
    { "allergen": "Milk",    "assertion": "Contains" },
    { "allergen": "Sesame",  "assertion": "MayContain", "note": "Shared prep area" }
  ]
}
Supported allergens (UK FSA 14)
// Celery, Cereals, Crustaceans, Egg, Fish, Lupins, Milk,
// Molluscs, Mustard, Nuts, Peanuts, Sesame, Soya, Sulphites

5. Create a modifier group

A modifier group (e.g. "Pizza toppings") holds a set of option groups (e.g. "Meats", "Veggies"). Attach the group to any product that needs it — change it once, it updates everywhere.

POST
Request
POST /organizations/{organizationId}/modifiergroups
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "Pizza Toppings",
  "minSelections": 0,
  "maxSelections": 8,
  "optionGroups": [
    { "optionGroupId": "c1a2-meats-...",   "displayOrder": 1 },
    { "optionGroupId": "c1a2-veggies-...", "displayOrder": 2 },
    { "optionGroupId": "c1a2-cheese-...",  "displayOrder": 3 }
  ]
}

6. Bulk price change — the pattern

The API has no "upload a CSV" endpoint — you integrate your tools directly. List products, filter in your code, issue pricing updates in parallel. Tens of thousands of prices roll out in a few minutes.

GET
Step 1 — list products
GET /organizations/{organizationId}/products?page=1&pageSize=500
Authorization: Bearer <access_token>
Step 2 — for each, PUT the new price
// Loop client-side. Respect rate limits (standard limits suit
// every integration we've seen). 202 Accepted is your signal to
// move on — propagation is asynchronous, never block on it.

PUT /organizations/{organizationId}/products/{id}/pricing
PUT /organizations/{organizationId}/products/{id}/pricing
PUT /organizations/{organizationId}/products/{id}/pricing
// ... carry on until the batch is done.

Need to sync the whole menu into a group's internal catalogue system? Talk to us about menu versioning and snapshot exports.

Other topics in the developer guide.

Running a central catalogue across hundreds of sites?

That's exactly what the menu API is for. Tell us what you're syncing from and we'll send credentials plus a technical contact.

Talk to our team