Appearance
Menu Items
Overview
Menu Items are the products customers can order. Each item has a name, description, price, image, and can have modifiers attached for customization.
Key Purpose: Define what products are available for customers to order.
Purpose
This page lets you create, edit, and manage the products customers can order, including their pricing, images, allergens, and modifier group assignments.
Key Concepts
- Item: A single orderable product with a name, description, price, image, category, tax rate, and optional modifier groups.
- Prep Time: Each item can have its own preparation time (in seconds internally); if not set, the location's average prep time is used automatically.
- Allergens & Dietary Preferences: Arrays of values stored per item for EU-compliant allergen disclosure and dietary filtering (vegetarian, vegan, halal, etc.).
- Alcohol Handling: Items flagged as containing alcohol automatically receive a minimum-age restriction based on the location's country code and the alcohol type (beer/wine vs spirits).
- Modifier Group Sync: Items are linked to modifier groups through a sync operation; all attached modifier groups must share the same label as the item.
Actions
Create Item
Add a new menu item by providing a name, price, category, tax rate, and optional image, allergens, dietary preferences, and modifier groups. The item is saved and synced to external integrations (e.g., Square) if active.
Edit Item
Update any item field including name, description, price, image, allergens, or modifier group assignments. Changes trigger a menu reload event for all ordering channels.
Delete Item
Remove an item permanently. Deletion also triggers a menu reload and syncs the removal to connected integrations.
Export Items
Export the full item list for a location to a spreadsheet with columns for name, type, PLU, price, tax amounts, label, and status.
Location
- Backoffice Route:
/menus/items - Backend Controller:
app/Http/Controllers/Api/ItemController.php - Vue Component:
src/views/items/Items.vue - Item Form:
src/components/forms/items/ItemForm.vue
Fields
Item Name
| Property | Value |
|---|---|
| Field ID | name |
| Label | Item Name |
| Type | Text |
| Required | Yes |
| Validation | `required |
Description: The name of the product shown to customers on menus.
Best Practices:
- Keep names concise but descriptive
- Include key ingredients for clarity
- Avoid abbreviations customers won't understand
Customer Impact:
- Online Ordering: Displayed in menu list and item detail
- Kiosk: Large text on item cards
- Receipt: Printed on order receipt
Examples:
- "Margherita Pizza"
- "Classic Cheeseburger"
- "Caesar Salad with Grilled Chicken"
Item Description
| Property | Value |
|---|---|
| Field ID | description |
| Label | Description |
| Type | Textarea |
| Required | No |
| Validation | `nullable |
Description: Detailed description of the item including ingredients, preparation method, or special notes.
Best Practices:
- List main ingredients
- Mention cooking style (grilled, fried, etc.)
- Note if spicy, vegetarian, etc.
- Keep under 150 characters for mobile readability
Customer Impact:
- Online Ordering: Shown below item name in detail view
- Kiosk: Displayed when item is selected
Examples:
- "Fresh mozzarella, San Marzano tomatoes, basil, extra virgin olive oil on hand-stretched dough"
- "100% beef patty, cheddar cheese, lettuce, tomato, pickles, special sauce on a brioche bun"
Price
| Property | Value |
|---|---|
| Field ID | price |
| Label | Price |
| Type | Currency |
| Required | Yes |
| Validation | `required |
Description: Base price of the item before any modifiers. This is the starting price shown to customers.
Business Logic:
- Final price = Base price + Selected modifier prices
- Price stored without tax (tax calculated separately)
- Displayed with currency symbol based on location settings
Customer Impact:
- Online Ordering: Price shown on menu and in cart
- Kiosk: Large price display on item cards
- Receipt: Base price + modifiers itemized
Examples:
- Pizza: €12.50
- Burger: €9.95
- Drink: €2.50
Category
| Property | Value |
|---|---|
| Field ID | category_id |
| Label | Category |
| Type | Select |
| Required | Yes |
| Validation | Must be valid category ID |
Description: The menu category this item belongs to. Determines where the item appears in the menu.
Business Logic:
- Item can only be in one category
- Category determines menu section
- Category sort order affects item visibility
Customer Impact:
- Online Ordering: Item appears under selected category tab
- Kiosk: Item grouped with category
Related: Categories
Image
| Property | Value |
|---|---|
| Field ID | content_id / cloudflare_image_id |
| Label | Product Image |
| Type | Image Upload |
| Required | No (but highly recommended) |
| Validation | Image file, max:10240 KB (~10MB); recommended 800x600px |
Description: Product photo shown to customers. Internally stored as a content_id (legacy content system) and/or a cloudflare_image_id (Cloudflare Images). The system prefers the Cloudflare image ID when available. High-quality images significantly increase sales.
Best Practices:
- Use professional food photography
- Consistent lighting and background
- Show the actual product (not stock photos)
- Square or 4:3 aspect ratio works best
- Minimum 800px width for quality display
Customer Impact:
- Online Ordering: Thumbnail in menu, large in detail view
- Kiosk: Large image on item cards (very important for kiosk UX)
- No Image: Placeholder shown, reduces appeal
Technical:
- Images are resized and optimized on upload
- Multiple sizes generated for different displays
- CDN delivery for fast loading
Status
| Property | Value |
|---|---|
| Field ID | status |
| Label | Status |
| Type | Select (string enum: Active, Inactive, Unavailable, Hidden) |
| Default | Active |
| Required | No |
Description: Whether the item is currently available for ordering. The backend uses a string status field (the ItemStatuses enum), not a boolean toggle.
Business Logic:
- Status =
Active→ Item shown normally - Status =
Unavailable→ Item's stock is written to 0 so it shows as "Sold Out" - Status =
Inactive/Hidden→ Item removed/hidden from the menu - Can be changed manually or automatically by inventory
Customer Impact:
- Online Ordering: Unavailable items hidden or grayed out
- Kiosk: Same behavior
- Existing Carts: Items may be removed if made unavailable
Use Cases:
- Seasonal items
- Out of stock
- Discontinued items
- Time-limited specials
Tax Rate
| Property | Value |
|---|---|
| Field ID | tax_rate_code |
| Label | Tax Rate |
| Type | Select |
| Required | Yes |
| Validation | Must be valid tax rate code |
Description: VAT/tax rate code applied to this item. Different items may have different tax rates (e.g., food vs drinks). The code is resolved against the location's country code to determine the actual rate.
Business Logic:
- Tax calculated using the tax rate code, the location's country code, and the dining option (dine-in, takeout, delivery)
- Tax shown separately on receipt
- Different rates for dine-in vs takeaway in some countries
Customer Impact:
- Online Ordering: Prices typically shown including tax
- Receipt: Tax breakdown shown
Examples:
- Food: 9% (reduced rate)
- Drinks: 21% (standard rate)
- Alcohol: 21% (standard rate)
Related: Tax Rates
Allergens
| Property | Value |
|---|---|
| Field ID | allergens |
| Label | Allergens |
| Type | Multi-select |
| Required | No (but legally required in EU) |
| Options | Gluten, Crustaceans, Eggs, Fish, Peanuts, Soybeans, Milk, Nuts, Celery, Mustard, Sesame, Sulphites, Lupin, Molluscs |
Description: Allergen information for the item. Required by law in EU for food businesses.
Business Logic:
- 14 major allergens defined by EU regulation
- Displayed as icons or text
- Searchable/filterable in some implementations
Customer Impact:
- Online Ordering: Allergen icons shown on item
- Kiosk: Allergen information displayed
- Receipt: May be printed for reference
Legal Note: In the EU, businesses must inform customers about allergens. This is a legal requirement, not optional.
Dietary Preferences
| Property | Value |
|---|---|
| Field ID | dietary_preferences |
| Label | Dietary Info |
| Type | Multi-select |
| Required | No |
| Options | Vegetarian, Vegan, Halal, Kosher, Gluten-Free, Dairy-Free, etc. |
Description: Dietary labels to help customers with specific dietary requirements find suitable items.
Customer Impact:
- Online Ordering: Filter menu by dietary preference
- Kiosk: Dietary icons displayed
- Search: Can search for "vegetarian" items
Preparation Time
| Property | Value |
|---|---|
| Field ID | prep_time_seconds |
| Label | Preparation Time |
| Type | Number |
| Unit | Seconds (stored internally) |
| Default | 0 |
| Required | No |
Description: Time needed to prepare this specific item, stored in seconds internally. The getPrepTimeMinutes() method converts to minutes by dividing by 60. If the value is 0, the location's average prep time is used as a fallback.
Business Logic:
- Stored in seconds internally, displayed in minutes to the user
- If
prep_time_secondsis 0, falls back to the location'saverage_prep_time - The maximum item prep time in an order is used (not the sum)
- Affects earliest available time slot
Use Cases:
- Slow-cooked items
- Made-to-order specials
- Complex dishes
Use Default Prep Time
| Property | Value |
|---|---|
| Field ID | use_default_prep_time |
| Label | (server-managed, not a user toggle) |
| Type | Boolean |
| Default | false |
| Required | No |
Description: A server-managed flag (not a form toggle). When the item is saved, the backend sets use_default_prep_time to false if an explicit prep time was supplied, or to true (and copies the location's average prep time into prep_time_seconds) when no prep time was entered.
Business Logic:
- Flag is set in
ItemServicebased on whetherprep_time_secondsis present in the request - If no prep time is entered → flag becomes
trueand the location's average prep time is used - Maximum item prep time in an order is used (not the sum)
Example:
- Order has: Burger (5 min), Pizza (15 min), Drink (1 min)
- If all use item prep time: Order prep = max(5, 15, 1) = 15 min
- Added to location's base prep time
PLU (Product Lookup Unit)
| Property | Value |
|---|---|
| Field ID | plu |
| Label | PLU / Product Code |
| Type | Text |
| Required | No |
Description: Product Lookup Unit - internal product code for POS integration, inventory, and reporting. Indexed in the database for search.
Use Cases:
- POS integration
- Inventory management
- Reporting and exports
- Third-party integrations
Modifiers
| Property | Value |
|---|---|
| Field ID | modifier_group_ids |
| Label | Modifier Groups |
| Type | Array of Modifier Group IDs |
| Required | No |
Description: Modifier groups attached to this item. Allows customers to customize the item.
Business Logic:
- Item can have multiple modifier groups
- Each group can be required or optional
- Modifier prices added to base price
Customer Impact:
- Online Ordering: Modifier selection shown when adding to cart
- Kiosk: Modifier screens after item selection
Related: Modifiers
Business Logic
Price Calculation
Base Price (item.price)
+
Modifier 1 Price Adjustment
+
Modifier 2 Price Adjustment
+
...
=
Item Total
Item Total × Quantity = Line Total
Sum of all Line Totals = Subtotal
Subtotal + Delivery Fee = Order TotalAvailability Logic
Is item available?
│
├── item.status = Unavailable → Sold Out (stock forced to 0)
│
├── item.status = Inactive / Hidden → Removed/hidden from menu
│
├── item.category.visible = false → Hidden
│
├── Inventory tracking enabled?
│ └── Stock = 0 → Sold Out
│
└── Menu availability schedule?
└── Outside schedule → HiddenSearch & Filter
Items can be searched/filtered by:
- Name (text search)
- Category
- Allergens (exclude items with allergen)
- Dietary preferences (include items with preference)
- Price range
- Availability
Customer Impact
Online Ordering
- Menu View: Items displayed in category sections
- Item Card: Image, name, short description, price
- Item Detail: Full description, allergens, modifiers
- Add to Cart: Select modifiers, quantity, add
Kiosk
- Category Selection: Large category buttons
- Item Grid: Large images with name and price
- Item Selection: Full-screen item with modifiers
- Customization: Step through modifier groups
KDS (Kitchen Display)
- Item name displayed
- Selected modifiers shown
- Special instructions included
- Quantity highlighted
Receipt
- Item name
- Selected modifiers (indented)
- Unit price
- Quantity
- Line total
Relations
Depends On
- Categories: Items must belong to a category
- Tax Rates: Items must have a tax rate
- Modifier Groups: Optional customization
Affects
- Order Capacity: Items counted for capacity limits
- Inventory: Stock decremented on order
- Reports: Sales tracked per item
- Offers: Can be targeted by promotions
Related Features
Business Rules
- An item's modifier groups must all share the same label as the item itself; mismatched labels are rejected with a 400 error.
- If
prep_time_secondsis 0, thegetPrepTimeMinutes()method automatically falls back to the location's average prep time. On save,ItemServicesetsuse_default_prep_timetofalsewhen an explicit prep time is supplied, or totrue(copying the location's average prep time intoprep_time_seconds) when none is provided. - Items containing alcohol are automatically assigned a minimum purchase age based on the location's country code and alcohol type (e.g., 16 for beer/wine in Belgium, 21 for any alcohol in the US).
- A category can be set to null on an item, but items without a category will not appear in any menu structure.
- Deleting or updating an item triggers a
ReloadMenuevent so all active ordering channels (online ordering, kiosk) reflect the change immediately.
FAQs
Can an item belong to multiple categories? No. Each item has a single
category_idand appears under only one category in the menu.What happens if I do not upload an image? The item will display with a placeholder image on all channels. Items with images have significantly higher conversion rates.
How is the final price calculated? Final price = item base price + sum of all selected modifier price adjustments. Tax is calculated separately based on the item's
tax_rate_code, the location's country code, and the dining option (dine-in, takeout, delivery).Does changing an item's price update existing carts? Changes trigger a menu reload, but in-progress carts may still show the old price until the customer refreshes or proceeds to checkout.
Can I bulk-import items? There is no bulk import via the backoffice UI, but items can be synced from Square if the integration is active. You can also export items to a spreadsheet for review.
"What's the difference between the Active, Inactive, Unavailable, and Hidden statuses?" An item's Status (in the right-hand Status card) has four values: Active (shown and orderable), Inactive, Unavailable, and Hidden. Setting an item to Unavailable writes its stock to 0 so it shows as sold out; Hidden removes it from the menu. Both fire a menu reload so every channel updates immediately.
"Can I sell an item at a different price on different channels (e.g. Uber Eats vs in-store)?" Yes. In the Pricing card, open the custom-price modal and add per-channel prices on top of the base price. Each channel you pick (Kiosk, POS, Online Ordering, Table QR Ordering, Uber Eats, Takeaway, Shopify) gets its own price field; channels with no custom price use the base price.
"How do I control which sales channels an item appears on?" Use the Channels checkboxes in the right column of the item form. The available channels come from your location's configuration and include Kiosk, POS, Online Ordering, Table QR Ordering, Uber Eats, Takeaway, and Shopify.
"What is the 'Apply to all locations' switch when creating an item?" Toggling it on creates the item as a global item shared across all your locations. It is only available when creating a new item, not when editing.
"How do I limit how many of one item a customer can order?" Set the Max Item Order Limit field (in QTY) on the Product Specifications tab. If left blank, no per-item cap is enforced.
"Why can't I create or edit items in the back office for some locations?" When a location is connected to a POS provider (Square, MplusKassa, Kassanet, ShopCaisse, Lightspeed), new-item creation is blocked and certain fields are locked because they're owned by the POS — the menu syncs from the POS instead. The locked fields vary by provider.
"How does the 'Contains alcohol' setting work?" Turn on Contains Alcohol on the Product Specifications tab and choose Beer & Wine or Spirits. The system assigns a minimum purchase age automatically based on your location's country and the alcohol type (e.g. 16 for beer/wine and 18 for spirits in Belgium; 21 for any alcohol in the US).
"Do I have to enter a PLU, and where does the suggested one come from?" A PLU is required, but you don't have to type it — the form auto-generates one from the item name plus a timestamp, which you can overwrite. If left blank, the backend also generates one on save.
"How do prep times work and what unit do I enter?" Enter the preparation time in minutes on the Product Specifications tab; it defaults to your location's average prep time. For an order, the longest item prep time is used, not the sum.
"How do I add upsell suggestions to an item?" Open the Upsell Groups tab on the item form, search for and add existing upsell groups, then drag to set their order. Upsell groups are created under Marketing; this tab only assigns ones you've already built.
"What gets included when I export my items?" The export spreadsheet has columns for Name, Type, PLU, Price, Dine In / Take Out / Delivery Tax Amount, Label, and Status. Variant-group child items appear as indented rows under their group.
"What are the AI 'Suggested' chips I see when I type an item name?" As you type the name, the system looks up a reference database and suggests an image, description, calories, allergens, dietary preferences, supplements, prep time, and whether it contains alcohol. You can Accept All, Decline All, or approve/dismiss each one; nothing is applied until you accept it.
Troubleshooting
Problem: Item not showing on menu
Causes:
- Item
statusis not active - Category is hidden
- Item not assigned to active menu
- Menu not assigned to location
Solutions:
- Enable item availability
- Check category visibility
- Verify menu contains item's category
- Assign menu to location
Problem: Price showing incorrectly
Causes:
- Wrong price entered
- Tax display setting (incl/excl)
- Modifier prices not configured
Solutions:
- Update item price
- Check tax display settings
- Review modifier price adjustments
Problem: Image not displaying
Causes:
- No image uploaded
- Image too large (failed upload)
- Unsupported format
- CDN/caching issue
Solutions:
- Upload an image
- Resize image before upload
- Convert to JPG/PNG/WebP
- Clear cache or wait for CDN refresh
Problem: Item shows "Sold Out" unexpectedly
Causes:
- Inventory tracking enabled with 0 stock
- Item manually set to unavailable
- Category disabled
Solutions:
- Restock item in Inventory
- Enable item availability
- Enable category
Examples
Simple Item (No Modifiers)
json
{
"name": "French Fries",
"description": "Crispy golden fries with sea salt",
"price": 3.50,
"category_id": "sides",
"cloudflare_image_id": "abc123",
"status": "active",
"tax_rate_code": "food-9",
"allergens": [],
"dietary_preferences": ["vegetarian", "vegan"],
"modifier_group_ids": []
}Item with Modifiers
json
{
"name": "Build Your Own Burger",
"description": "Start with our signature beef patty and customize to your taste",
"price": 8.95,
"category_id": "burgers",
"cloudflare_image_id": "def456",
"status": "active",
"tax_rate_code": "food-9",
"allergens": ["gluten", "eggs"],
"dietary_preferences": [],
"modifier_group_ids": [
"burger-size-id",
"burger-cheese-id",
"burger-toppings-id",
"burger-sauce-id"
]
}Item with Long Prep Time
json
{
"name": "Slow-Roasted Ribs",
"description": "Fall-off-the-bone pork ribs, smoked for 6 hours, served with coleslaw and fries",
"price": 18.95,
"category_id": "mains",
"cloudflare_image_id": "ghi789",
"status": "active",
"tax_rate_code": "food-9",
"allergens": ["celery", "mustard"],
"prep_time_seconds": 1800
}Seasonal/Limited Item
json
{
"name": "Pumpkin Spice Latte",
"description": "Limited time! Espresso with pumpkin spice and steamed milk",
"price": 4.95,
"category_id": "drinks",
"cloudflare_image_id": "jkl012",
"status": "active",
"tax_rate_code": "drinks-21",
"allergens": ["milk"],
"dietary_preferences": []
}