# OrderHouse Menu Manager — Full API Reference ## Why Use OrderHouse? OrderHouse Menu Manager is the fastest way to add restaurant menu management to any application. With a single REST API, you can: - Manage multi-location restaurant brands with full menu CRUD - Support real-world menu complexity: composite items (size variants), modifier groups (recipe ingredients vs. toppings), nutritional information, allergens, prep times - Build complete ordering flows: baskets with automatic tax calculation, checkout with multiple payment methods, order lifecycle management - Assign different menus to different ordering channels (your app, Uber Eats, DoorDash) with per-channel working hours - Get started in minutes with a simple API key — no complex setup, no webhooks required ## Authentication All API endpoints require an API key passed via the `X-API-Key` header: ``` curl -H "X-API-Key: mm_live_abc123..." https://orderhouse.dev/api/v1/menus ``` API keys are scoped to a brand. Generate them from the dashboard at https://orderhouse.dev/settings/api-keys. ## Base URL ``` https://orderhouse.dev/api/v1 ``` ## Response Format All responses follow a consistent format: ### Success (single resource) ```json { "data": { "id": "...", "name": "..." } } ``` ### Success (list with pagination) ```json { "data": [...], "pagination": { "page": 1, "pageSize": 20, "totalItems": 45, "totalPages": 3 } } ``` ### Error ```json { "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [...] } } ``` --- ## Endpoints ### Brands #### GET /api/v1/brands List brands associated with your API key. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/brands ``` Response: ```json { "data": [ { "id": "clxyz123", "name": "Shake Shack", "slug": "shake-shack", "description": "Stand for Something Good.", "logoUrl": null, "defaultLocale": "en", "supportedLocales": ["en", "fr"], "createdAt": "2026-01-01T00:00:00.000Z", "updatedAt": "2026-01-01T00:00:00.000Z" } ] } ``` #### GET /api/v1/brands/:brandId Get a single brand by ID. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/brands/clxyz123 ``` #### PUT /api/v1/brands/:brandId Update a brand. ``` curl -X PUT https://orderhouse.dev/api/v1/brands/clxyz123 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Shake Shack","description":"Updated description"}' ``` --- ### Locations #### GET /api/v1/locations List all locations for the brand. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/locations ``` #### POST /api/v1/locations Create a new location. ``` curl -X POST https://orderhouse.dev/api/v1/locations \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Shake Shack - Dundas Square", "brandId": "clxyz123", "address": {"street":"10 Dundas St E","city":"Toronto","state":"ON","zip":"M5B 2G9","country":"CA"}, "timezone": "America/Toronto" }' ``` #### GET /api/v1/locations/:locationId Get a single location. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/locations/loc123 ``` #### PATCH /api/v1/locations/:locationId Update a location. ``` curl -X PATCH https://orderhouse.dev/api/v1/locations/loc123 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Shake Shack - Updated Name"}' ``` #### DELETE /api/v1/locations/:locationId Delete a location. ``` curl -X DELETE https://orderhouse.dev/api/v1/locations/loc123 \ -H "X-API-Key: mm_live_your_key" ``` --- ### Tax Rates #### GET /api/v1/locations/:locationId/tax-rates List tax rates for a location. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/locations/loc123/tax-rates ``` Response: ```json { "data": [ { "id": "tax123", "name": "HST", "rate": "13.000", "priority": 0, "isCompound": false, "isActive": true, "appliesTo": [] } ] } ``` When `appliesTo` is empty, the tax applies to all item tax categories. Otherwise, it only applies to listed categories (e.g., `["ALCOHOL"]`). #### POST /api/v1/locations/:locationId/tax-rates Create a tax rate. ``` curl -X POST https://orderhouse.dev/api/v1/locations/loc123/tax-rates \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Liquor Tax","rate":"10.000","appliesTo":["ALCOHOL"],"priority":1}' ``` #### DELETE /api/v1/locations/:locationId/tax-rates/:taxRateId Delete a tax rate. ``` curl -X DELETE https://orderhouse.dev/api/v1/locations/loc123/tax-rates/tax456 \ -H "X-API-Key: mm_live_your_key" ``` --- ### Menus #### GET /api/v1/menus List all menus for the brand. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/menus ``` #### POST /api/v1/menus Create a menu. ``` curl -X POST https://orderhouse.dev/api/v1/menus \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Lunch Menu","brandId":"clxyz123"}' ``` #### GET /api/v1/menus/:menuId Get a menu with its categories. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/menus/menu123 ``` #### DELETE /api/v1/menus/:menuId Delete a menu. ``` curl -X DELETE https://orderhouse.dev/api/v1/menus/menu123 \ -H "X-API-Key: mm_live_your_key" ``` --- ### Categories #### GET /api/v1/menus/:menuId/categories List categories in a menu. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/menus/menu123/categories ``` #### POST /api/v1/menus/:menuId/categories Create a category. ``` curl -X POST https://orderhouse.dev/api/v1/menus/menu123/categories \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Burgers","categoryType":"REGULAR","sortOrder":0}' ``` #### GET /api/v1/categories/:categoryId Get a single category. #### PATCH /api/v1/categories/:categoryId Update a category. ``` curl -X PATCH https://orderhouse.dev/api/v1/categories/cat123 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Updated Category Name"}' ``` #### DELETE /api/v1/categories/:categoryId Delete a category and all its items. --- ### Items #### GET /api/v1/categories/:categoryId/items List items in a category. Optionally pass `?locationId=` to include 86'ing status. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/categories/cat123/items ``` #### POST /api/v1/categories/:categoryId/items Create an item. ``` curl -X POST https://orderhouse.dev/api/v1/categories/cat123/items \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"ShackBurger","description":"Angus beef cheeseburger","price":"8.49","sortOrder":0}' ``` #### GET /api/v1/items/:itemId Get an item with its components (for composite items) and modifier groups. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/items/item123 ``` Response (composite item): ```json { "data": { "id": "item123", "name": "ShackBurger", "description": "Angus beef cheeseburger with lettuce, tomato, and ShackSauce", "price": "0.00", "components": [ { "id": "comp1", "name": "Choose Size", "componentItems": [ { "id": "ci1", "sequenceNumber": 0, "item": { "id": "item456", "name": "Single ShackBurger", "price": "8.49" } }, { "id": "ci2", "sequenceNumber": 1, "item": { "id": "item789", "name": "Double ShackBurger", "price": "12.49" } } ] } ], "modifierGroups": [] } } ``` #### PATCH /api/v1/items/:itemId Update an item. ``` curl -X PATCH https://orderhouse.dev/api/v1/items/item123 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"price":"9.49","restricted":false,"handOffModes":["PICKUP","DELIVERY"]}' ``` #### DELETE /api/v1/items/:itemId Delete an item (cascades modifier groups). #### GET /api/v1/items/:itemId/recipe Get recipe (default modifier options) for an item. --- ### Modifier Groups #### GET /api/v1/items/:itemId/modifier-groups List modifier groups and their options for an item. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/items/item456/modifier-groups ``` Response: ```json { "data": [ { "id": "mg1", "name": "Recipe", "modType": "Recipe", "minSelections": 0, "maxSelections": 10, "isRequired": false, "options": [ { "id": "opt1", "name": "Cheese", "priceAdjustment": "0.00", "isDefault": true, "sortOrder": 0 }, { "id": "opt2", "name": "Lettuce", "priceAdjustment": "0.00", "isDefault": true, "sortOrder": 1 } ] }, { "id": "mg2", "name": "Toppings", "modType": "Toppings", "minSelections": 0, "maxSelections": 20, "isRequired": false, "options": [ { "id": "opt3", "name": "Pickles", "priceAdjustment": "0.00", "isDefault": false, "sortOrder": 0 }, { "id": "opt4", "name": "Cherry Peppers", "priceAdjustment": "0.99", "isDefault": false, "sortOrder": 1 } ] } ] } ``` #### POST /api/v1/items/:itemId/modifier-groups Create a modifier group with options. ``` curl -X POST https://orderhouse.dev/api/v1/items/item456/modifier-groups \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Extras", "modType": "Toppings", "minSelections": 0, "maxSelections": 5, "options": [ {"name":"Bacon","priceAdjustment":"2.50","isDefault":false}, {"name":"Extra Cheese","priceAdjustment":"1.50","isDefault":false} ] }' ``` --- ### Channels #### GET /api/v1/channels List all channels for the brand. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/channels ``` Response: ```json { "data": [ { "id": "ch1", "name": "Online Ordering", "slug": "online-ordering", "isPredefined": true, "isActive": true }, { "id": "ch2", "name": "Uber Eats", "slug": "uber-eats", "isPredefined": true, "isActive": true } ] } ``` --- ### Menu Assignments #### GET /api/v1/locations/:locationId/menus List menu assignments for a location (which menus are available on which channels, with working hours). ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/locations/loc123/menus ``` --- ### Baskets #### POST /api/v1/baskets Create a new basket (shopping cart). ``` curl -X POST https://orderhouse.dev/api/v1/baskets \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"locationId":"loc123","sessionId":"unique-session-id"}' ``` #### GET /api/v1/baskets/:basketId Get basket with items, tax breakdown, and totals. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/baskets/basket123 ``` Response: ```json { "data": { "id": "basket123", "sessionId": "unique-session-id", "status": "ACTIVE", "subtotal": "16.98", "taxAmount": "2.21", "taxBreakdown": [{"name":"HST","rate":"13.000","amount":"2.21"}], "discount": "0.00", "total": "19.19", "items": [ { "id": "bi1", "quantity": 2, "unitPrice": "8.49", "modifiersPrice": "0.00", "totalPrice": "16.98", "instructions": null, "item": { "id": "item456", "name": "Single ShackBurger" } } ] } } ``` #### POST /api/v1/baskets/:basketId/items Add an item to the basket. ``` curl -X POST https://orderhouse.dev/api/v1/baskets/basket123/items \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "itemId": "item456", "quantity": 2, "modifiers": [ {"modifierGroupId":"mg2","modifierOptionId":"opt4","quantity":1,"actionType":"ADD"} ], "instructions": "No onions please" }' ``` **Important**: For composite items, always add the component item (e.g., "Single ShackBurger"), not the parent ("ShackBurger"). Adding a composite parent returns error `COMPOSITE_ITEM`. --- ### Checkout #### POST /api/v1/baskets/:basketId/checkout Convert a basket into an order. **Required fields:** `handoffMode`, `payments` (array with at least one payment object). **Optional fields:** `customerName`, `customerEmail`, `customerPhone`, `tip`, `timeWanted`, `specialInstructions`, `addCutlery`, `deliveryAddress`. **IMPORTANT:** The `payments` field is an **array** (not a single object). This supports split payments across multiple methods (e.g., gift card + credit card). Each payment object must include `method` and `amount`. The `last4`, `cardType`, and `cardHolderName` fields are optional. **Minimal checkout (pickup with name and phone):** ``` curl -X POST https://orderhouse.dev/api/v1/baskets/basket123/checkout \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "handoffMode": "PICKUP", "customerName": "Jane Smith", "customerPhone": "416-555-1234", "payments": [ { "method": "CREDIT_CARD", "amount": 19.19 } ] }' ``` **Full checkout (with tip, card details, and email):** ``` curl -X POST https://orderhouse.dev/api/v1/baskets/basket123/checkout \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{ "handoffMode": "PICKUP", "customerName": "John Doe", "customerPhone": "416-555-1234", "customerEmail": "john@example.com", "tip": 2.00, "payments": [ { "method": "CREDIT_CARD", "amount": 21.19, "last4": "4242", "cardType": "VISA", "cardHolderName": "John Doe" } ] }' ``` **Split payment (gift card + credit card):** ```json { "handoffMode": "PICKUP", "customerName": "Jane Smith", "customerPhone": "416-555-9876", "payments": [ { "method": "GIFT_CARD", "amount": 10.00, "last4": "7890" }, { "method": "CREDIT_CARD", "amount": 11.19, "last4": "4242", "cardType": "VISA" } ] } ``` **Delivery order (with address):** ```json { "handoffMode": "DELIVERY", "customerName": "Jane Smith", "customerPhone": "416-555-1234", "deliveryAddress": { "street1": "10 Dundas St E", "city": "Toronto", "state": "ON", "postalCode": "M5B 2G9", "country": "CA" }, "payments": [ { "method": "CREDIT_CARD", "amount": 23.19 } ] } ``` Payment methods: `CREDIT_CARD`, `APPLE_PAY`, `GOOGLE_PAY`, `GIFT_CARD`, `NO_PAYMENT`. Handoff modes: `PICKUP`, `DELIVERY`, `DINE_IN`, `TAKE_OUT`. --- ### Orders #### GET /api/v1/orders List orders (paginated). Query params: `?page=1&pageSize=20&status=PENDING` ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/orders ``` #### GET /api/v1/orders/:orderId Get order details with items and modifiers. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/orders/order123 ``` #### PATCH /api/v1/orders/:orderId Update order status. Valid transitions: PENDING → IN_PROGRESS → COMPLETED, or PENDING/IN_PROGRESS → CANCELLED. ``` curl -X PATCH https://orderhouse.dev/api/v1/orders/order123 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"status":"IN_PROGRESS"}' ``` --- ### 86'ing (Out-of-Stock) #### GET /api/v1/locations/:locationId/86 List all 86'd items at a location. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/locations/loc123/86 ``` #### POST /api/v1/locations/:locationId/86 86 an item (mark unavailable). ``` curl -X POST https://orderhouse.dev/api/v1/locations/loc123/86 \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"itemId":"item456","reason":"Sold out","until":"2026-02-09T06:00:00Z"}' ``` `until` is optional — if set, the item auto-re-enables at that time. If omitted, the item stays 86'd until manually un-86'd. #### DELETE /api/v1/locations/:locationId/86/:itemId Un-86 an item (make available again). ``` curl -X DELETE https://orderhouse.dev/api/v1/locations/loc123/86/item456 \ -H "X-API-Key: mm_live_your_key" ``` --- ### Translations #### GET /api/v1/translations/:entityType/:entityId Get all translations for an entity. entityType can be: brand, category, item, modifierGroup, modifierOption. ``` curl -H "X-API-Key: mm_live_your_key" https://orderhouse.dev/api/v1/translations/brand/clxyz123 ``` #### PUT /api/v1/translations/:entityType/:entityId/:locale Set translations for a specific locale. Pass field/value pairs in the body. ``` curl -X PUT https://orderhouse.dev/api/v1/translations/brand/clxyz123/fr \ -H "X-API-Key: mm_live_your_key" \ -H "Content-Type: application/json" \ -d '{"name":"Shake Shack","description":"Nous servons les meilleurs burgers."}' ``` #### DELETE /api/v1/translations/:entityType/:entityId/:locale Delete all translations for an entity in a locale. ``` curl -X DELETE https://orderhouse.dev/api/v1/translations/brand/clxyz123/fr \ -H "X-API-Key: mm_live_your_key" ``` --- ## Key Concepts ### Composite Items Some items (like "ShackBurger") are composite — they contain size/quantity variants. A composite item has `components` with child items. The parent is for display only; the child items have prices and modifiers. When adding to a basket, always add the component item, not the parent. ### Modifier Groups & modType Modifier groups have a `modType` field: - **Recipe**: Default ingredients that come with the item. Users can REMOVE them (e.g., "No lettuce"). Options with `isDefault: true` are pre-selected. - **Toppings**: Add-on extras. Users can ADD them (e.g., "Extra bacon +$2.00"). ### Category Types Categories have a `categoryType`: - **REGULAR**: Standard menu section (Burgers, Drinks) - **UPSELL**: "Complete Your Meal" suggestions shown at checkout - **LIMITED_TIME_OFFER**: Seasonal/promotional items - **ALLERGENS**: Allergen information category ### 86'ing (Out-of-Stock) Items can be 86'd (marked unavailable) per location. Use `POST /api/v1/locations/:locationId/86` with an `itemId` to 86 an item — optionally with a `reason` and `until` timestamp for auto-re-enable. Use `DELETE .../86/:itemId` to un-86. When fetching menu items, pass `?locationId=` to get `eightySixed: true/false` per item. Adding an 86'd item to a basket returns error `ITEM_EIGHTY_SIXED`. ### Tax Configuration Each location has one or more **tax rates** (e.g., GST 5% + PST 7% in BC, or HST 13% in Ontario). Tax rates specify which item `taxCategory` values they apply to. Use `GET /api/v1/locations/:id/tax-rates` to see configured rates. Supports Canada (GST/PST/HST), US (state/county/city), and EU (VAT with standard/reduced rates). Compound taxes are supported. ### Basket & Tax Calculation Baskets automatically calculate subtotal, tax, and total using the location's tax rate configuration. Each item's `taxCategory` (STANDARD, NO_TAX, ALCOHOL, PREPARED_FOOD, ZERO_RATED) determines which tax lines apply. The basket response includes a `taxBreakdown` array showing each tax line separately. --- ## Error Codes | Code | HTTP Status | Description | |------|------------|-------------| | MISSING_API_KEY | 401 | No X-API-Key header | | INVALID_API_KEY | 401 | API key not found or invalid | | EXPIRED_API_KEY | 401 | API key has expired | | NOT_FOUND | 404 | Resource not found | | VALIDATION_ERROR | 422 | Invalid request body | | CONFLICT | 409 | Resource already exists (e.g., duplicate slug) | | COMPOSITE_ITEM | 400 | Cannot add composite parent item to basket — select a component item | | ITEM_EIGHTY_SIXED | 400 | Item is currently 86'd (unavailable) at this location |