حمل التطبيق الخريطة (تفتح في نافذة جديدة)سجل منشأتك

المطورون

الكتالوج

القوائم والمنتجات

انشر القائمة كاملة بصيغة CM v1، وعدلها بالفروقات، وتابع النسخ والبصمات. ولماذا للمتاجر نموذج منتجات مستقل بمتغيرات ومخزون.

قريباواجهة الشركاء لم تفتح بعد. هذه الوثائق لتخطط لربطك من الآن، والعناوين والمفاتيح تصدر عند الإطلاق.سجل كمطور ←

في هذه الصفحة

نقاط النهاية والصلاحيات

الطلب الصلاحية
PUT /places/{place_id}/menu catalog:write
PATCH /places/{place_id}/menu catalog:write
DELETE /places/{place_id}/menu catalog:write
GET /places/{place_id}/menu catalog:write
GET /places/{place_id}/menu/hashes catalog:write
POST /menus/reconcile catalog:write
/places/{place_id}/products… قريبا catalog:write

نموذجان، لا نموذج واحد

القائمة (مطاعم ومقاه): وثيقة صغيرة تقرأ كاملة. فيها أقسام، وأصناف بأحجام، ومجموعات إضافات لها حد أدنى وحد أعلى، ووجبات مركبة، وجداول أوقات. تنشر وتعدل كوحدة واحدة لها رقم نسخة.

المنتجات (متاجر): كتالوج كبير بآلاف الأصناف، يتصفح بالتصنيفات والفلاتر. كل وحدة تباع متغير له SKU وباركود وسعر ومخزون خاص. لا يرسل كوثيقة واحدة، بل على دفعات.

لو دمجناهما لخسرت القوائم قواعد الإضافات، أو خسرت المتاجر مخزون كل متغير. لذلك لكل منهما نموذج. ولكل مكان مصدر واحد: قائمة أو منتجات.

صيغة القائمة CM v1

json
{
  "schema": "aurmap.menu/1",
  "currency": "SAR",
  "default_language": "ar",
  "languages": ["ar", "en"],
  "menu_url": "https://order.partner.example/al-rayhan-olaya",
  "identity": {
    "name": { "ar": "مطعم الريحان", "en": "Al Rayhan" },
    "tagline": { "ar": "برجر وعصائر طازجة", "en": "Burgers and fresh juice" },
    "logo": "https://cdn.partner.example/logo.png",
    "cover": "https://cdn.partner.example/cover.jpg",
    "slider": ["https://cdn.partner.example/slide-1.jpg"],
    "template": "classic",
    "theme": "custom",
    "colors": { "background": "#F2F4F7", "text": "#041731", "card": "#FFFFFF", "accent": "#C8102E", "on_accent": "#FFFFFF" },
    "font": "Tajawal",
    "button_shape": "rounded"
  },
  "tax": { "prices_include_vat": true, "vat_percent": 15, "service_charge_percent": 0 },
  "aurmap_ordering": { "enabled": true, "pickup": true, "delivery": false, "cash": true },
  "sections": [
    { "id": "burgers", "parent_id": null, "name": { "ar": "البرجر", "en": "Burgers" }, "image": null, "sort": 0, "schedule_id": null },
    { "id": "juices", "parent_id": null, "name": { "ar": "العصائر", "en": "Juices" }, "image": null, "sort": 1, "schedule_id": null },
    { "id": "desserts", "parent_id": null, "name": { "ar": "الحلى", "en": "Desserts" }, "image": null, "sort": 2, "schedule_id": "evening" },
    { "id": "meals", "parent_id": null, "name": { "ar": "الوجبات", "en": "Meals" }, "image": null, "sort": 3, "schedule_id": null }
  ],
  "items": [
    {
      "id": "chicken-burger",
      "type": "menu_item",
      "section_id": "burgers",
      "sort": 0,
      "name": { "ar": "برجر دجاج", "en": "Chicken burger" },
      "description": { "ar": "صدر دجاج مقرمش، خس، صوص البيت", "en": "Crispy chicken breast, lettuce, house sauce" },
      "image": "https://cdn.partner.example/img/chicken-burger.jpg",
      "price_minor": 2300,
      "original_price_minor": null,
      "calories": 540,
      "prep_min": 10,
      "prep_max": 15,
      "label": { "name": { "ar": "الأكثر طلبا", "en": "Best seller" } },
      "warnings": [ { "id": "gluten", "name": { "ar": "يحتوي على الغلوتين", "en": "Contains gluten" }, "icon": null } ],
      "sizes": [
        { "id": "m", "name": { "ar": "وسط", "en": "Medium" }, "price_minor": 2300, "original_price_minor": null, "calories": 540 },
        { "id": "l", "name": { "ar": "كبير", "en": "Large" }, "price_minor": 2990, "original_price_minor": null, "calories": 720 }
      ],
      "group_ids": ["sauce", "extras"],
      "order_types": { "pickup": true, "delivery": true, "dine_in": true },
      "recommended_ids": ["orange-juice"],
      "schedule_id": null,
      "order_url": null
    },
    {
      "id": "orange-juice",
      "type": "menu_item",
      "section_id": "juices",
      "sort": 1,
      "name": { "ar": "عصير برتقال", "en": "Orange juice" },
      "price_minor": 920,
      "sizes": [
        { "id": "s", "name": { "ar": "صغير", "en": "Small" }, "price_minor": 920 },
        { "id": "l", "name": { "ar": "كبير", "en": "Large" }, "price_minor": 1380 }
      ],
      "group_ids": [],
      "order_types": { "pickup": true, "delivery": true, "dine_in": true }
    },
    {
      "id": "kunafa",
      "type": "menu_item",
      "section_id": "desserts",
      "sort": 2,
      "name": { "ar": "كنافة", "en": "Kunafa" },
      "price_minor": 1840,
      "sizes": [],
      "group_ids": [],
      "order_types": { "pickup": true, "delivery": true, "dine_in": true }
    },
    {
      "id": "chicken-burger-meal",
      "type": "menu_item",
      "section_id": "meals",
      "sort": 3,
      "name": { "ar": "وجبة برجر دجاج", "en": "Chicken burger meal" },
      "price_minor": 3000,
      "original_price_minor": 3220,
      "sizes": [],
      "group_ids": ["sauce"],
      "order_types": { "pickup": true, "delivery": true, "dine_in": true }
    }
  ],
  "groups": [
    {
      "id": "sauce", "name": { "ar": "الصوص", "en": "Sauce" }, "required": true, "min": 1, "max": 1,
      "options": [
        { "id": "hot", "name": { "ar": "صوص حار", "en": "Hot sauce" }, "price_minor": 230, "image": null },
        { "id": "none", "name": { "ar": "بدون صوص", "en": "No sauce" }, "price_minor": 0, "image": null }
      ]
    },
    {
      "id": "extras", "name": { "ar": "إضافات", "en": "Extras" }, "required": false, "min": 0, "max": 2,
      "options": [
        { "id": "cheese", "name": { "ar": "جبن إضافي", "en": "Extra cheese" }, "price_minor": 345, "image": null },
        { "id": "no-onion", "name": { "ar": "بدون بصل", "en": "No onion" }, "price_minor": 0, "image": null },
        { "id": "pickles", "name": { "ar": "مخلل إضافي", "en": "Extra pickles" }, "price_minor": 0, "image": null }
      ]
    }
  ],
  "combos": [
    { "item_id": "chicken-burger-meal", "components": [ { "item_id": "chicken-burger", "quantity": 1 }, { "item_id": "orange-juice", "quantity": 1 } ] }
  ],
  "schedules": [
    { "id": "evening", "days": [0, 1, 2, 3, 4, 5, 6], "from": "16:00", "to": "02:00" }
  ],
  "state": {
    "paused": false,
    "sold_out": { "items": ["kunafa"], "sizes": [], "options": [] },
    "prices": { "items": [], "sizes": [], "options": [] }
  }
}

كل المبالغ بالهللة: 2990 تعني 29.90 ريال. وكل نص خريطة لغات.

الحقول العليا

الحقل الوصف
schema aurmap.menu/1. اختياري
currency رمز العملة. الافتراضي SAR
default_language، languages اللغة الأساسية، وكل لغات النصوص. الافتراضي ar
menu_url رابط https للطلب من نظامك، يظهر حين يكون الطلب من AurMap متوقفا
identity هوية القائمة. اختياري. بدونه نستعمل هوية المكان على AurMap
tax prices_include_vat وvat_percent وservice_charge_percent، كما هي في قائمة التاجر
aurmap_ordering ما تستقبله قائمتك: enabled وpickup وdelivery وcash
sections الأقسام
items الأصناف. مطلوب
groups مجموعات الإضافات، مرة واحدة لكل مجموعة
combos الوجبات المركبة
schedules أوقات يطلب فيها قسم أو صنف
state الحالة: إيقاف، نفاد، أسعار مؤقتة. منفصلة عن البنية

لا ترسل version ولا hash ولا بصمة الأصناف. نحن نحسبها ونعيدها لك.

الهوية

الحقل القيم
name، tagline خريطة لغات
logo، cover، slider[] روابط صور https
template classic أو editorial أو darkluxe أو delivery
theme light أو dark أو custom
colors مع custom: background وtext وcard وaccent وon_accent بصيغة #RRGGBB
font اسم الخط
button_shape rounded أو pill أو square

القسم

الحقل الوصف
id معرفك للقسم
parent_id القسم الأب لقسم فرعي، أو null
name، image، sort الاسم والصورة والترتيب
schedule_id جدول يحدد متى يطلب من القسم، أو null

الصنف يذكر قسمه في section_id. القسم لا يذكر أصنافه.

الصنف

الحقل الوصف
id معرفك الثابت للصنف، حتى 64 حرفا من [A-Za-z0-9_.-]. يعود لك في الطلبات كما هو
product_id رقم المنتج الداخلي في نظامك إن اختلف عن id. اختياري
type menu_item أو product أو service
section_id، sort القسم والترتيب
name، description، image الاسم والوصف والصورة
price_minor السعر. مع الأحجام هو أقل سعر حجم، للعرض فقط
original_price_minor السعر قبل الخصم، يظهر مشطوبا
calories، prep_min، prep_max السعرات، ومدة التجهيز بالدقائق
label شارة مثل «الأكثر طلبا»: { "name": {…} }
warnings[] تنبيهات مثل مسببات الحساسية: id وname وicon
sizes[] الأحجام. انظر الجدول التالي
group_ids[] مجموعات الإضافات التي تنطبق على الصنف
order_types pickup وdelivery وdine_in
recommended_ids[] أصناف نقترحها مع هذا الصنف
schedule_id جدول خاص بالصنف، أو null
order_url رابط طلب خاص بالصنف. اختياري

الحجم

الحقل الوصف
id معرفك للحجم
name الاسم
price_minor سعر الصنف كاملا بهذا الحجم، لا فرق السعر
original_price_minor، calories اختياريان

مجموعة الإضافات

الحقل الوصف
id، name المعرف والاسم
required هل الاختيار إلزامي
min، max أقل وأكثر عدد خيارات. max يساوي 0 يعني بلا حد
options[] id وname وprice_minor (يضاف إلى السطر) وimage

الوجبة المركبة

item_id هو صنف الوجبة نفسه، وله سعره في items. وcomponents[] مكوناتها: item_id وquantity.

الجدول

الحقل الوصف
id معرف الجدول
days[] أيام الأسبوع أرقاما: 0 الأحد، 1 الاثنين، حتى 6 السبت
from، to HH:MM بتوقيت المكان

إذا كان to أصغر من from فالفترة تمتد بعد منتصف الليل، وتحسب لليوم الذي بدأت فيه. مثال: days: [4] من 16:00 إلى 02:00 يعني من الخميس الساعة 4 مساء حتى الجمعة الساعة 2 فجرا.

الحالة

الحقل الوصف
paused true يوقف الطلب من القائمة كلها
sold_out items[] معرفات أصناف، وsizes[] من item_id وsize_id، وoptions[] من group_id وoption_id
prices أسعار مؤقتة فوق أسعار البنية: items[] وsizes[] وoptions[]، ولكل منها price_minor

تحديث الحالة وحدها في التوفر.

القواعد

  • كل معرف مذكور يجب أن يكون موجودا: القسم، والمجموعات، والجداول، والأصناف المقترحة، ومكونات الوجبات، وما في الحالة.
  • required: true يعني أن min واحد فأكثر. وmax إما 0 أو لا يقل عن min.
  • الأيام من 0 إلى 6، والوقت من 00:00 إلى 23:59، وfrom لا يساوي to.
  • كل معرفاتك، للأصناف والأحجام والمجموعات والخيارات والأقسام والجداول، حتى 64 حرفا من [A-Za-z0-9_.-]. النقطتان : مرفوضتان لأنهما محجوزتان عندنا.
  • كل الروابط https فقط، ولعناوين عامة على الإنترنت.
  • الحد الأقصى: 2 ميغابايت، و3000 صنف، و300 مجموعة، و50 خيارا في المجموعة.

أي مخالفة ترد 422 MENU_INVALID مع مكان الخطأ:

json
{
  "error": {
    "code": "MENU_INVALID",
    "message": "The menu has 2 problems.",
    "request_id": "req_01JAA2",
    "details": [
      { "path": "items[0].section_id", "code": "UNKNOWN_SECTION", "message": "Section breakfast does not exist." },
      { "path": "groups[1]", "code": "MIN_ABOVE_MAX", "message": "min is above max." }
    ]
  }
}

نشر القائمة كاملة

PUT /places/{place_id}/menu بصلاحية catalog:write.

bash
IDEMPOTENCY_KEY="menu-olaya-2026-10-14-01" \
aurmap PUT /v1/places/plc_9q3w7e/menu "$(cat menu.json)"

202

json
{ "place_id": "plc_9q3w7e", "version": 42, "prev_version": 41, "kind": "full", "status": "processing", "hash": "9f2c4be1d07a65e3c1f8a2b4d6e8f0a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3", "warnings": [] }
  • نتحقق من الصيغة فورا. ثم نجهز الصور وننشر القائمة في ثوان، ويصلك menu.changed بالنسخة والبصمة نفسيهما.
  • الصورة التي يتعذر جلبها لا تفشل النشر. نتجاوزها ونذكرها في warnings.
  • إذا أرسلت قائمة مطابقة للحالية، نرد 200 مع "status": "unchanged" ولا ننشئ نسخة جديدة.
  • لتجنب الكتابة فوق تغيير لم تره، أرسل If-Match: "41". إذا لم تكن النسخة الحالية 41 نرد 409 VERSION_CONFLICT.
js
import { readFile } from 'node:fs/promises';

const menu = JSON.parse(await readFile('menu.json', 'utf8'));
const res = await aurmap('PUT', '/v1/places/plc_9q3w7e/menu', menu, 'menu-olaya-2026-10-14-01');
console.log(res.version, res.hash);
php
$menu = json_decode(file_get_contents('menu.json'), true);
$res = aurmap('PUT', '/v1/places/plc_9q3w7e/menu', $menu, 'menu-olaya-2026-10-14-01');
echo $res['version'], ' ', $res['hash'];

تعديل البنية بالفروقات

PATCH /places/{place_id}/menu بصلاحية catalog:write.

bash
aurmap PATCH /v1/places/plc_9q3w7e/menu '{"base_version":43,"upsert":[{"id":"mint-lemonade","type":"menu_item","section_id":"juices","sort":4,"name":{"ar":"ليموناضة بالنعناع","en":"Mint lemonade"},"price_minor":1150,"sizes":[],"group_ids":[],"order_types":{"pickup":true,"delivery":true,"dine_in":true}}],"remove":["kunafa"],"groups":[{"id":"extras","name":{"ar":"إضافات","en":"Extras"},"required":false,"min":0,"max":3,"options":[{"id":"cheese","name":{"ar":"جبن إضافي","en":"Extra cheese"},"price_minor":345,"image":null},{"id":"no-onion","name":{"ar":"بدون بصل","en":"No onion"},"price_minor":0,"image":null},{"id":"pickles","name":{"ar":"مخلل إضافي","en":"Extra pickles"},"price_minor":0,"image":null},{"id":"jalapeno","name":{"ar":"هالبينو","en":"Jalapeno"},"price_minor":230,"image":null}]}]}'
json
{ "place_id": "plc_9q3w7e", "version": 44, "prev_version": 43, "kind": "structure", "status": "processing", "hash": "b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2", "warnings": [] }
الحقل الوصف
base_version النسخة التي بنيت عليها التعديل. مطلوب
upsert[] أصناف كاملة. الصنف الموجود يستبدل كله
remove[] معرفات أصناف تحذف
groups[] مجموعات كاملة، تضاف أو تستبدل
remove_groups[] معرفات مجموعات تحذف
sections[]، combos[]، schedules[] إذا أرسلت أحدها استبدل كله
identity، tax، aurmap_ordering إذا أرسلت أحدها استبدل كله
state الحالة، بقواعد التوفر

إذا تغيرت القائمة بعد base_version نرد 409 VERSION_CONFLICT مع current_version. عندها اقرأ القائمة أو انشرها كاملة.

إزالة القائمة

DELETE /places/{place_id}/menu يزيل قائمتك من المكان، ويصلك menu.removed. بعدها يرد GET /places/{place_id}/menu بـ 404 MENU_NOT_FOUND.

النسخ والبصمات

الطلب يرجع
GET /places/{place_id}/menu النسخة الحالية، وبصمتها، ووقت نشرها، والقائمة كاملة مع الحالة وبصمة كل صنف
GET /places/{place_id}/menu?version=42 نسخة سابقة ما دامت محفوظة. وإلا 404 MENU_VERSION_GONE
GET /places/{place_id}/menu/hashes بصمة القائمة وبصمة كل صنف
POST /menus/reconcile يقارن ما عندك بما عندنا لعدة أماكن مرة واحدة
  • version يزيد واحدا مع كل تغيير مقبول: نشر كامل، أو تعديل بنية، أو تحديث حالة.
  • hash نص من 64 حرفا. قارنه بالمساواة فقط.

مرة كل ليلة أرسل آخر نسخة وبصمة تعرفهما لكل مكان، حتى 500 مكان في الطلب:

bash
aurmap POST /v1/menus/reconcile '{"places":[{"place_id":"plc_9q3w7e","version":44,"hash":"b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2"}]}'
json
{ "stale": [] }

كل مكان يظهر في stale اقرأ قائمته أو انشرها كاملة من جديد.

الصور

  • نجلب كل صورة مرة واحدة ونحفظها عندنا بعدة مقاسات.
  • نجلب من عناوين https عامة فقط. نرفض العناوين الخاصة والمحلية، مثل 10.x و192.168.x و127.0.0.1 و169.254.x، ولا نتبع التحويلات. الصورة المرفوضة نتجاوزها مع تحذير IMAGE_URL_NOT_ALLOWED.
  • لتغيير صورة، غير عنوانها.
  • الحجم حتى 8 ميغابايت، بصيغة JPEG أو PNG أو WebP.

المنتجات للمتاجر (CP v1) قريبا

نموذج المنتجات للمتاجر ومنصات التجارة الإلكترونية. كل منتج له محاور خيارات (المقاس، اللون)، ومتغيرات لكل منها SKU وسعر ومخزون.

json
{
  "schema": "cp/1",
  "id": "tshirt-basic",
  "name": { "ar": "تيشيرت قطن", "en": "Cotton T-shirt" },
  "category_ids": ["men", "tshirts"],
  "images": [ { "url": "https://cdn.partner.example/p/tshirt.jpg" } ],
  "option_axes": [
    { "id": "size", "name": { "ar": "المقاس", "en": "Size" }, "values": [ { "id": "m", "name": { "ar": "M", "en": "M" } }, { "id": "l", "name": { "ar": "L", "en": "L" } } ] }
  ],
  "variants": [
    { "id": "tshirt-basic-m", "sku": "TS-M-BLK", "barcode": "6281234567890", "options": { "size": "m" }, "price_minor": 7900, "stock": { "tracked": true, "quantity": 12 } },
    { "id": "tshirt-basic-l", "sku": "TS-L-BLK", "options": { "size": "l" }, "price_minor": 7900, "stock": { "tracked": true, "quantity": 0 } }
  ],
  "status": "active"
}
الطلب الوصف
PUT /places/{place_id}/products/{product_id} إضافة أو استبدال منتج
POST /places/{place_id}/products/batch حتى 500 منتج في طلب، ونتيجة لكل منتج
DELETE /places/{place_id}/products/{product_id} حذف منتج
GET /places/{place_id}/products المنتجات صفحة بصفحة
PUT /places/{place_id}/product-categories شجرة التصنيفات
GET /places/{place_id}/products/hashes بصمة كل منتج للمطابقة