Appearance
Menus
The Menu API provides access to restaurant menus, categories, items, and modifiers.
List Menus
Get all available menus for a concept at a location.
GET /v1/ecommerce/locations/{location}/concepts/{concept}/menusAuthentication: None (Public)
Rate Limit: 100/min per IP
Path Parameters
| Parameter | Type | Description |
|---|---|---|
location | string | Location hashkey |
concept | string | Concept hashkey |
Response
json
{
"status": "success",
"data": [
{
"menu_id": "mnu_00000k1L2m3N4o5",
"name": "All Day Menu",
"description": "Our complete menu available all day",
"menu_type": "regular",
"hero_url": "https://cdn.example.com/menus/allday.jpg",
"status": "live",
"display_order": 1,
"channel_restrictions": []
},
{
"menu_id": "mnu_00000p1Q2r3S4t5",
"name": "Breakfast Menu",
"description": "Morning favorites served until 11am",
"menu_type": "breakfast",
"hero_url": "https://cdn.example.com/menus/breakfast.jpg",
"status": "live",
"display_order": 2,
"availability_rules": {
"days": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"],
"time_ranges": [{"start": "06:00", "end": "11:00"}]
},
"channel_restrictions": ["kiosk", "online"]
}
]
}Menu Fields
| Field | Type | Description |
|---|---|---|
menu_id | string | Unique menu identifier |
name | string | Display name |
description | string | Menu description |
menu_type | string | regular, breakfast, lunch, dinner, late_night, happy_hour |
hero_url | string | Menu banner image |
status | string | draft, live, archived |
display_order | integer | Sort order |
availability_rules | object | When menu is available |
channel_restrictions | array | Channels where menu is available (empty = all) |
Get Menu Items
Retrieve the full menu hierarchy including categories, subcategories, and items.
GET /v1/ecommerce/locations/{location}/concepts/{concept}/menus/{menu}/itemsAuthentication: None (Public)
Rate Limit: 100/min per IP
Path Parameters
| Parameter | Type | Description |
|---|---|---|
location | string | Location hashkey |
concept | string | Concept hashkey |
menu | string | Menu hashkey |
Response
json
{
"status": "success",
"data": {
"menu": {
"menu_id": "mnu_00000k1L2m3N4o5",
"name": "All Day Menu",
"hero_url": "https://cdn.example.com/menus/allday.jpg"
},
"hierarchy": [
{
"item_id": "itm_cat_appetizers",
"name": "Appetizers",
"description": "Start your meal right",
"type": "category",
"display_order": 1,
"hero_url": "https://cdn.example.com/categories/appetizers.jpg",
"children": [
{
"item_id": "itm_sub_fried",
"name": "Fried Favorites",
"type": "subcategory",
"display_order": 1,
"children": [
{
"item_id": "itm_00000a1B2c3D4e5",
"name": "Mozzarella Sticks",
"description": "Six golden fried mozzarella sticks served with marinara",
"type": "item",
"base_price": 8.99,
"hero_url": "https://cdn.example.com/items/mozz.jpg",
"nutritional_info": {
"calories": 450,
"protein": 18,
"carbs": 35,
"fat": 28,
"sodium": 890,
"sugar": 3
},
"allergens": ["dairy", "gluten", "eggs"],
"dietary_tags": ["vegetarian"],
"tags": ["popular", "shareable"],
"prep_time_minutes": 8,
"is_featured": true,
"is_86d": false,
"display_order": 1,
"status": "live"
},
{
"item_id": "itm_00000f5G6h7I8j9",
"name": "Onion Rings",
"description": "Crispy battered onion rings",
"type": "item",
"base_price": 6.99,
"hero_url": "https://cdn.example.com/items/onion-rings.jpg",
"allergens": ["gluten"],
"dietary_tags": ["vegetarian", "vegan"],
"is_featured": false,
"is_86d": false,
"display_order": 2,
"status": "live"
}
]
}
]
},
{
"item_id": "itm_cat_entrees",
"name": "Entrees",
"type": "category",
"display_order": 2,
"children": []
}
]
}
}Item Fields
| Field | Type | Description |
|---|---|---|
item_id | string | Unique item identifier |
name | string | Display name |
description | string | Item description |
type | string | category, subcategory, or item |
base_price | decimal | Price before modifiers |
hero_url | string | Main item image |
nutritional_info | object | Nutrition facts |
allergens | array | Allergen warnings |
dietary_tags | array | Diet compatibility |
tags | array | Custom tags (popular, signature, etc.) |
prep_time_minutes | integer | Estimated prep time |
is_featured | boolean | Featured item flag |
is_86d | boolean | Out of stock flag |
display_order | integer | Sort order within parent |
status | string | draft or live |
children | array | Child items (for categories) |
Allergens
Common allergen values:
| Value | Description |
|---|---|
dairy | Contains milk products |
eggs | Contains eggs |
fish | Contains fish |
shellfish | Contains shellfish |
tree_nuts | Contains tree nuts |
peanuts | Contains peanuts |
wheat | Contains wheat |
gluten | Contains gluten |
soy | Contains soy |
sesame | Contains sesame |
Dietary Tags
| Value | Description |
|---|---|
vegetarian | No meat |
vegan | No animal products |
gluten-free | No gluten |
keto | Keto-friendly |
halal | Halal certified |
kosher | Kosher certified |
Get Item Details
Get detailed item information including modifiers/customizations.
GET /v1/ecommerce/locations/{location}/concepts/{concept}/items/{item}Authentication: None (Public)
Rate Limit: 100/min per IP
Response
json
{
"status": "success",
"data": {
"item_id": "itm_00000a1B2c3D4e5",
"name": "Classic Burger",
"description": "1/3 lb beef patty with lettuce, tomato, onion, and pickles",
"type": "item",
"base_price": 12.99,
"hero_url": "https://cdn.example.com/items/burger.jpg",
"gallery_images": [
{"url": "https://cdn.example.com/items/burger-1.jpg", "alt_text": "Burger front view"},
{"url": "https://cdn.example.com/items/burger-2.jpg", "alt_text": "Burger side view"}
],
"nutritional_info": {
"calories": 750,
"protein": 42,
"carbs": 45,
"fat": 48
},
"allergens": ["gluten", "dairy", "eggs"],
"dietary_tags": [],
"prep_time_minutes": 12,
"is_featured": true,
"is_86d": false,
"option_groups": [
{
"option_group_id": "opg_00000k1L2m3N4o5",
"name": "Choose Your Cheese",
"description": "Select your cheese preference",
"is_required": true,
"min_selections": 1,
"max_selections": 1,
"sort_order": 1,
"modifiers": [
{
"modifier_id": "mod_american",
"name": "American Cheese",
"price_adjustment": 0,
"is_default": true,
"sort_order": 1
},
{
"modifier_id": "mod_cheddar",
"name": "Cheddar Cheese",
"price_adjustment": 0,
"sort_order": 2
},
{
"modifier_id": "mod_swiss",
"name": "Swiss Cheese",
"price_adjustment": 0.50,
"sort_order": 3
},
{
"modifier_id": "mod_none",
"name": "No Cheese",
"price_adjustment": 0,
"sort_order": 4
}
]
},
{
"option_group_id": "opg_00000p5Q6r7S8t9",
"name": "Add Toppings",
"description": "Customize with extra toppings",
"is_required": false,
"min_selections": 0,
"max_selections": 5,
"sort_order": 2,
"modifiers": [
{
"modifier_id": "mod_bacon",
"name": "Bacon",
"price_adjustment": 2.00,
"sort_order": 1
},
{
"modifier_id": "mod_avocado",
"name": "Avocado",
"price_adjustment": 1.50,
"sort_order": 2
},
{
"modifier_id": "mod_jalapeno",
"name": "Jalapenos",
"price_adjustment": 0.50,
"sort_order": 3
},
{
"modifier_id": "mod_egg",
"name": "Fried Egg",
"price_adjustment": 1.50,
"sort_order": 4
}
]
}
]
}
}Option Group Fields
| Field | Type | Description |
|---|---|---|
option_group_id | string | Unique option group identifier |
name | string | Display name |
description | string | Instructions for customer |
is_required | boolean | Must select before adding to cart |
min_selections | integer | Minimum choices required |
max_selections | integer | Maximum choices allowed |
sort_order | integer | Display order |
modifiers | array | Available choices |
Modifier Fields
| Field | Type | Description |
|---|---|---|
modifier_id | string | Unique modifier identifier |
name | string | Display name |
price_adjustment | decimal | Price to add (positive) or subtract (negative) |
is_default | boolean | Pre-selected by default |
sort_order | integer | Display order |
Check Item Availability
Batch check if items are available (not 86'd).
POST /v1/ecommerce/locations/{location}/items/check-availabilityAuthentication: OAuth Token
Scope: kitchenclick:menus.read
Rate Limit: 200/min per client
Request Body
json
{
"items": [
{ "item_id": "itm_00000a1B2c3D4e5", "quantity": 2 },
{ "item_id": "itm_00000f5G6h7I8j9", "quantity": 1 }
],
"order_type": "pickup"
}| Field | Type | Required | Description |
|---|---|---|---|
items | array | Yes | Items to check |
items[].item_id | string | Yes | Menu item ID |
items[].quantity | integer | No | Quantity (echoed through) |
scheduled_at | string | No | Check availability for a future time |
order_type | string | No | Order type; flags items paused for that type |
Response
data is an array, one entry per requested item.
json
{
"status": "success",
"data": [
{
"item_id": "itm_00000a1B2c3D4e5",
"name": "Classic Burger",
"available": true,
"is_86d": false,
"reason": null,
"expected_back_at": null,
"price": 14.99
},
{
"item_id": "itm_00000f5G6h7I8j9",
"name": "Side Salad",
"available": false,
"is_86d": true,
"reason": "86d",
"expected_back_at": "2026-06-18T00:00:00Z",
"price": 5.99
}
],
"meta": { "location": "loc_00000k1L2m3N4o5" }
}| Field | Type | Description |
|---|---|---|
item_id | string | Menu item ID |
available | boolean | Whether the item can currently be ordered |
is_86d | boolean | true if 86'd (out of stock) |
reason | string | null | Unavailability reason, e.g. 86d, order_type_paused, not_live |
expected_back_at | string | null | When the item is expected back, if known |
price | number | Effective price at this location |
Example: Build Menu UI
javascript
async function loadMenuData(locationId, conceptId) {
// Get all menus
const menusResponse = await fetch(
`/api/v1/ecommerce/locations/${locationId}/concepts/${conceptId}/menus`
);
const { data: menus } = await menusResponse.json();
// Filter to currently available menus
const now = new Date();
const availableMenus = menus.filter(menu => {
if (menu.status !== 'live') return false;
if (!menu.availability_rules) return true;
const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'lowercase' });
const currentTime = now.toTimeString().slice(0, 5);
const { days, time_ranges } = menu.availability_rules;
if (!days.includes(dayOfWeek)) return false;
return time_ranges.some(range =>
currentTime >= range.start && currentTime <= range.end
);
});
// Load items for first available menu
if (availableMenus.length > 0) {
const menuId = availableMenus[0].menu_id;
const itemsResponse = await fetch(
`/api/v1/ecommerce/locations/${locationId}/concepts/${conceptId}/menus/${menuId}/items`
);
const { data } = await itemsResponse.json();
return {
menus: availableMenus,
currentMenu: data.menu,
hierarchy: data.hierarchy,
};
}
return { menus: [], currentMenu: null, hierarchy: [] };
}Changelog
| Date | Change |
|---|---|
| 2026-06-17 | Corrected response ID fields to the _id convention (menu_id, item_id, option_group_id, modifier_id); fixed the Check Item Availability request/response to the real items[].item_id shape. |
| 2026-01-15 | Initial publication. |