# Get the full menu for a location Returns the complete menu for a location, including all categories, items, and nested modifier groups. Menu structure: - A menu contains categories (e.g., "Deli & Sandwiches", "Beverages") - Each category contains items (e.g., "Build Your Own Sub Sandwich") - Each item may have modifier groups (e.g., "Bread Choice", "Protein") - Each modifier group contains modifiers (e.g., "White", "Wheat") - Modifiers can have nested modifier groups (up to 3 levels deep) Caching: Use the version_hash field to detect menu changes. For lightweight change detection without downloading the full menu, use the GET /locations/{location_id}/menu/metadata endpoint. Response size: Menu responses can be large (100KB+). Cache aggressively and use the metadata endpoint for polling. Endpoint: GET /locations/{location_id}/menu Version: 1.0.0 Security: oauth2 ## Path parameters: - `location_id` (string, required) Unique identifier for the location. ## Response 200 fields (application/json): - `location_id` (string, required) The location this menu belongs to. - `last_modified` (string, required) ISO 8601 timestamp of the last menu modification (UTC). Use this with the version_hash for cache invalidation. Example: "2026-01-15T14:30:00Z" - `version_hash` (string, required) Opaque hash representing the current menu version. Compare against your cached hash to detect changes without fetching the full menu. Use the GET /locations/{location_id}/menu/metadata endpoint for lightweight change detection. Example: "sha256:a1b2c3d4e5f6" - `categories` (array, required) Menu categories with their items and modifier groups. - `categories.id` (string, required) Unique identifier for this category. - `categories.name` (string, required) Display name of the category. Example: "Deli & Sandwiches" - `categories.description` (string,null) Optional description of the category. Example: "Fresh-made sandwiches and deli items." - `categories.sort_order` (integer, required) Display order for this category. Lower values appear first. Use this to render categories in the intended order. Example: 1 - `categories.items` (array, required) Menu items in this category. - `categories.items.id` (string, required) Unique identifier for this menu item. - `categories.items.name` (string, required) Display name of the item. Example: "Build Your Own Sub Sandwich" - `categories.items.description` (string,null) Optional description of the item. Example: "Choose your bread, protein, and toppings." - `categories.items.base_price` (object, required) Base price before any modifier adjustments. The final item price is base_price + sum of selected modifier price_adjustments. - `categories.items.base_price.amount` (integer, required) Monetary value in the smallest currency unit (cents for USD). Example: 1299 = $12.99. Example: 1299 - `categories.items.base_price.currency` (string, required) ISO 4217 currency code. Example: "USD" - `categories.items.image_url` (string,null) URL to an image of the item. Null if no image is available. Images are served via CDN and are suitable for display in apps. Example: "https://cdn.tote.ai/images/items/sub-sandwich.jpg" - `categories.items.available` (boolean, required) Whether this item is currently available for ordering. Items may become unavailable due to stock levels or time restrictions. - `categories.items.modifier_groups` (array) Modifier groups available for customizing this item. Groups define selection rules (min/max) and contain the available modifiers. - `categories.items.modifier_groups.id` (string, required) Unique identifier for this modifier group. - `categories.items.modifier_groups.name` (string, required) Display name (e.g., "Bread Choice", "Sauce Selection"). Example: "Bread Choice" - `categories.items.modifier_groups.min_selections` (integer, required) Minimum number of modifiers the customer must select. Set to 0 for optional groups, 1 or more for required groups. Example: 1 - `categories.items.modifier_groups.max_selections` (integer, required) Maximum number of modifiers the customer may select. Set to 1 for single-select groups, higher for multi-select. Example: 1 - `categories.items.modifier_groups.allows_duplicates` (boolean) Whether the same modifier can be selected multiple times. Example: selecting "Extra Cheese" twice to get double cheese. - `categories.items.modifier_groups.modifiers` (array, required) Available modifiers in this group. - `categories.items.modifier_groups.modifiers.id` (string, required) Unique identifier for this modifier. - `categories.items.modifier_groups.modifiers.name` (string, required) Display name (e.g., "Cheddar", "Ranch Dressing"). Example: "Cheddar" - `categories.items.modifier_groups.modifiers.price_adjustment` (object, required) Additional cost for this modifier. Use amount=0 when the modifier is included in the base price. - `categories.items.modifier_groups.modifiers.is_default` (boolean) Whether this modifier is pre-selected by default. Useful for common selections like "Regular" size. - `categories.items.modifier_groups.modifiers.modifier_groups` (array) Nested modifier groups for multi-level customization (max 3 levels deep). Example: Sandwich > Protein > Steak Preparation > Sauce Selection. - `categories.items.non_discountable` (boolean) Whether this item is excluded from all discount calculations. When true, promo codes, automatic discounts, and loyalty rewards will not apply to this item. Display "Not eligible for discounts" in your UI for these items. The server enforces this during price calculation. - `categories.items.age_verification_required` (boolean) Whether this item requires age verification before purchase. Applies to tobacco, alcohol, and other age-restricted products. - `categories.items.minimum_age` (integer,null) Minimum age required to purchase this item (e.g., 21 for alcohol, 21 for tobacco in most US states). Null if no age restriction. Example: 21 - `categories.items.daily_sales_start_time` (string,null) Earliest time this item can be sold, in HH:MM format (24-hour, location local time). Null if no time restriction. Example: breakfast items available starting at "06:00". Example: "06:00" - `categories.items.daily_sales_end_time` (string,null) Latest time this item can be sold, in HH:MM format (24-hour, location local time). Null if no time restriction. Example: breakfast items stop selling at "10:30". Example: "10:30" - `categories.items.max_allowed_quantity` (integer,null) Maximum quantity of this item allowed per transaction. Null if no limit. Used for regulatory compliance (e.g., tobacco purchase limits, pseudoephedrine quantity restrictions). Example: 2 - `categories.items.tender_restriction_level` (string,null) Whether this item has payment method restrictions. When RESTRICTED, check the allowed_tenders field for accepted payment methods. Tolerant Reader: New restriction level values may be added in future versions. Treat unknown values as RESTRICTED (check allowed_tenders) rather than assuming unrestricted. Enum: "NONE", "RESTRICTED" - `categories.items.allowed_tenders` (array,null) Payment methods accepted for this item. Null means all payment methods are accepted. When populated, only the listed tenders can be used to pay for this item. Example: tobacco items may exclude EBT per SNAP regulations. Tolerant Reader: New tender values may be added in future versions. Treat unknown values as unrestricted (allow payment) rather than restricted. Enum: "CREDIT_CARD", "DEBIT_CARD", "CASH", "GIFT_CARD", "LOYALTY_POINTS", "DIGITAL_WALLET", "EBT" ## Response 401 fields (application/json): - `error` (object, required) - `error.code` (string, required) Machine-readable error category. Use this field for programmatic error handling (e.g., retry on RATE_LIMIT_ERROR, re-authenticate on AUTHENTICATION_ERROR). Enum: "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR", "RATE_LIMIT_ERROR", "NOT_FOUND_ERROR", "CONFLICT_ERROR", "INTERNAL_ERROR" - `error.message` (string, required) Human-readable error description. Safe to display to developers in logs or debugging tools. Do not display to end users. - `error.detail` (string) Additional context about the error, including suggestions for resolution. May include specific field values or limits that were exceeded. - `error.request_id` (string, required) Unique identifier for this request. Include this value when contacting Tote Developer Support for troubleshooting. - `error.field` (string,null) JSON pointer to the field that caused the error. Null if the error is not field-specific. Example: "items[0].modifier_groups[1].modifiers" - `error.change_reasons` (array) Machine-readable reasons why the resource state changed, causing the conflict. Present on checkout 409 responses when expected_total does not match the current total. Clients should re-fetch the cart and call POST /carts/{cart_id}/calculate to get the updated total before retrying checkout. Enum: "PROMO_EXPIRED", "DISCOUNT_CHANGED", "ITEM_PRICE_CHANGED", "ITEM_UNAVAILABLE", "FEE_CHANGED" ## Response 404 fields (application/json): - `error` (object, required) - `error.code` (string, required) Machine-readable error category. Use this field for programmatic error handling (e.g., retry on RATE_LIMIT_ERROR, re-authenticate on AUTHENTICATION_ERROR). Enum: "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR", "RATE_LIMIT_ERROR", "NOT_FOUND_ERROR", "CONFLICT_ERROR", "INTERNAL_ERROR" - `error.message` (string, required) Human-readable error description. Safe to display to developers in logs or debugging tools. Do not display to end users. - `error.detail` (string) Additional context about the error, including suggestions for resolution. May include specific field values or limits that were exceeded. - `error.request_id` (string, required) Unique identifier for this request. Include this value when contacting Tote Developer Support for troubleshooting. - `error.field` (string,null) JSON pointer to the field that caused the error. Null if the error is not field-specific. Example: "items[0].modifier_groups[1].modifiers" - `error.change_reasons` (array) Machine-readable reasons why the resource state changed, causing the conflict. Present on checkout 409 responses when expected_total does not match the current total. Clients should re-fetch the cart and call POST /carts/{cart_id}/calculate to get the updated total before retrying checkout. Enum: "PROMO_EXPIRED", "DISCOUNT_CHANGED", "ITEM_PRICE_CHANGED", "ITEM_UNAVAILABLE", "FEE_CHANGED" ## Response 429 fields (application/json): - `error` (object, required) - `error.code` (string, required) Machine-readable error category. Use this field for programmatic error handling (e.g., retry on RATE_LIMIT_ERROR, re-authenticate on AUTHENTICATION_ERROR). Enum: "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR", "RATE_LIMIT_ERROR", "NOT_FOUND_ERROR", "CONFLICT_ERROR", "INTERNAL_ERROR" - `error.message` (string, required) Human-readable error description. Safe to display to developers in logs or debugging tools. Do not display to end users. - `error.detail` (string) Additional context about the error, including suggestions for resolution. May include specific field values or limits that were exceeded. - `error.request_id` (string, required) Unique identifier for this request. Include this value when contacting Tote Developer Support for troubleshooting. - `error.field` (string,null) JSON pointer to the field that caused the error. Null if the error is not field-specific. Example: "items[0].modifier_groups[1].modifiers" - `error.change_reasons` (array) Machine-readable reasons why the resource state changed, causing the conflict. Present on checkout 409 responses when expected_total does not match the current total. Clients should re-fetch the cart and call POST /carts/{cart_id}/calculate to get the updated total before retrying checkout. Enum: "PROMO_EXPIRED", "DISCOUNT_CHANGED", "ITEM_PRICE_CHANGED", "ITEM_UNAVAILABLE", "FEE_CHANGED" ## Response 500 fields (application/json): - `error` (object, required) - `error.code` (string, required) Machine-readable error category. Use this field for programmatic error handling (e.g., retry on RATE_LIMIT_ERROR, re-authenticate on AUTHENTICATION_ERROR). Enum: "AUTHENTICATION_ERROR", "INVALID_REQUEST_ERROR", "RATE_LIMIT_ERROR", "NOT_FOUND_ERROR", "CONFLICT_ERROR", "INTERNAL_ERROR" - `error.message` (string, required) Human-readable error description. Safe to display to developers in logs or debugging tools. Do not display to end users. - `error.detail` (string) Additional context about the error, including suggestions for resolution. May include specific field values or limits that were exceeded. - `error.request_id` (string, required) Unique identifier for this request. Include this value when contacting Tote Developer Support for troubleshooting. - `error.field` (string,null) JSON pointer to the field that caused the error. Null if the error is not field-specific. Example: "items[0].modifier_groups[1].modifiers" - `error.change_reasons` (array) Machine-readable reasons why the resource state changed, causing the conflict. Present on checkout 409 responses when expected_total does not match the current total. Clients should re-fetch the cart and call POST /carts/{cart_id}/calculate to get the updated total before retrying checkout. Enum: "PROMO_EXPIRED", "DISCOUNT_CHANGED", "ITEM_PRICE_CHANGED", "ITEM_UNAVAILABLE", "FEE_CHANGED"