Publishing menus
هذا المحتوى غير متوفر بلغتك بعد.
Your POS is where the restaurant manages its menu. Loops takes a copy of it and publishes it to the delivery apps. Read Menus first for the model.
PUT /brands/{id}/menu ──▶ import job ──▶ job.completed (succeeded | failed)POST /brands/{id}/menu/publish ──▶ publish job ──▶ job.completed (succeeded | partially_failed | failed)1. Build the menu from your system
Section titled “1. Build the menu from your system”Map your POS menu to the Loops structure:
| In your POS | In Loops |
|---|---|
| Menu group, section | Category |
| Item with one price | Product with price |
| Item with sizes | Product with variants |
| Add-ons, choices, extras | Modifier group with options, attached to products |
{ "categories": [ { "external_ref": "CAT-BURGERS", "name": { "en": "Burgers", "ar": "برجر" }, "sort_order": 1 } ], "modifier_groups": [ { "external_ref": "MG-EXTRAS", "name": { "en": "Extras", "ar": "إضافات" }, "min_selections": 0, "max_selections": 3, "options": [ { "external_ref": "OPT-CHEESE", "name": { "en": "Extra cheese", "ar": "جبنة إضافية" }, "price": { "amount": 300, "currency": "SAR" } }, { "external_ref": "OPT-JALAPENO", "name": { "en": "Jalapeño", "ar": "هالبينو" }, "price": { "amount": 200, "currency": "SAR" } } ] } ], "products": [ { "external_ref": "PRD-CHICKEN-BURGER", "category_external_ref": "CAT-BURGERS", "name": { "en": "Chicken Burger", "ar": "برجر دجاج" }, "description": { "en": "Crispy chicken, lettuce, house sauce", "ar": "دجاج مقرمش، خس، صوص خاص" }, "image_url": "https://cdn.example.sa/menu/chicken-burger.jpg", "calories": 540, "variants": [ { "external_ref": "VAR-CB-REG", "name": { "en": "Regular", "ar": "عادي" }, "price": { "amount": 2500, "currency": "SAR" } }, { "external_ref": "VAR-CB-LRG", "name": { "en": "Large", "ar": "كبير" }, "price": { "amount": 3200, "currency": "SAR" } } ], "modifier_group_external_refs": ["MG-EXTRAS"] } ]}Rules Loops checks on import:
- Every
external_refis unique within its type, and every reference points to something in the same request. - A product has either
priceor at least one variant, not both. - Names have both
enandar. max_selectionsis at leastmin_selections, and a modifier group has at least one option.- Images download, and are JPG or PNG of at least 800 × 800 px.
- The request is at most 5 MB.
2. Import
Section titled “2. Import”PUT /v1/brands/brd_9Lk3Vb/menu{ "id": "job_01J9R2K7TN", "type": "menu_import", "status": "pending", "created_at": "2026-10-06T10:00:00Z" }- An import replaces the whole menu. Anything you leave out is removed. Always send the complete menu.
- It is all or nothing. If anything is wrong, nothing changes, and the job’s
errorslist every problem:
{ "id": "job_01J9R2K7TN", "type": "menu_import", "status": "failed", "errors": [ { "external_ref": "PRD-FRIES", "field": "price", "code": "price_or_variants_required", "message": "Give a price or at least one variant." }, { "external_ref": "PRD-CHICKEN-BURGER", "field": "image_url", "code": "image_unreachable", "message": "The image URL returned 404." } ]}Wait for job.completed, or check GET /v1/jobs/{job_id}. Importing doesn’t change the delivery apps.
3. Publish
Section titled “3. Publish”POST /v1/brands/brd_9Lk3Vb/menu/publish
{ "location_ids": ["loc_2Xf9K"] }Leave out location_ids to publish to every location of the brand. Loops sends the menu to each active channel connection, and the job reports one result per location and delivery app:
{ "id": "job_01J9R3B1XQ", "type": "menu_publish", "status": "partially_failed", "results": [ { "location_id": "loc_2Xf9K", "channel_connection_id": "chc_5Rm1Pa", "channel": "delivery_app_a", "status": "succeeded" }, { "location_id": "loc_2Xf9K", "channel_connection_id": "chc_7Tq3Mn", "channel": "delivery_app_b", "status": "failed", "errors": [{ "external_ref": "PRD-CHICKEN-BURGER", "code": "channel_rejected_item", "message": "Description is too long for this delivery app." }] } ]}- Delivery apps review menus on their own schedule. A publish can take minutes, and results arrive as each app answers.
partially_failedmeans some delivery apps rejected some items. The rest were applied. Fix the items, import, and publish again.- Locations without an active channel connection are skipped; publish again after they go live.
Keeping the menu up to date
Section titled “Keeping the menu up to date”When the restaurant changes its menu in your POS, import and publish again. That includes:
-
prices;
-
new and removed items;
-
items that are temporarily unavailable (
available: false). -
Debounce changes. Publish once after a burst of edits, not after each keystroke.
-
Don’t publish more than once a minute per brand. Delivery apps limit how often a menu can change.
-
To check what Loops has,
GET /v1/brands/{brand_id}/menureturns the current menu and itsversion.
© 2026 شركة لووبز للتقنيات | Loops Technologies. جميع الحقوق محفوظة.
