الأماكن
الأماكن
اعرض قوائم الأماكن واجلب تفاصيل غنية لمكان واحد، بما في ذلك المراجعات والصور والأسئلة والأجوبة وذكاء الوصول (المدخل المُوصى به وإرشادات آخر 50 متراً).
النطاق المطلوب: places
center لمكان وlocation لكل مدخل هي سلاسل WKT بصيغة POINT(<lng> <lat>) — خط الطول أولاً. داخل arrivalIntelligence، تُعاد الإحداثيات المُحلَّلة على شكل كائنات { "lng": …, "lat": … }. وحقل rating هو رقم (مثل 4.6) في استجابتَي القائمة والتفاصيل معاً، بما يتّسق مع /search و/places/thumb.GET /places
اعرض قائمة الأماكن، مُرقَّمة بالصفحات، المُموَّل أولاً.
curl -s "https://navvo.io/api/v1/places?q=cafe&page=1&limit=20" \
-H "x-api-key: nvvo_YOUR_KEY"
معاملات الاستعلام
nameEn أو nameAr.1. القيمة الافتراضية 1 (الحدّ الأدنى 1).50، والحدّ الأقصى 100.true/1/yes/on) والزائفة (false/0/no/off) بشكل صحيح — فـ sponsored=false تعيد فعلاً الأماكن غير المُموَّلة فقط. والقيمة الفارغة أو غير المعروفة تتصرّف كمعامل محذوف (بلا تصفية).canonicalCategorySlug للمكان أو أي عنصر في مصفوفة categories لديه. اكتشف المعرّفات عبر GET /places/categories. عند ضبطها يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة category.wheelchair-accessible-entrance,free-parking-lot). يُطابَق المكان فقط إذا كانت كل السمات المطلوبة موجودة ومُفعّلة. قد يكون المعرّف خياراً (wi-fi) أو مجموعة كاملة (parking = يملك أي موقف). عند ضبطها يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة مصفوفة amenities المُطبّعة. اكتشف المعرّفات المتاحة عبر GET /places/facets.true، تُعاد فقط الأماكن المفتوحة حالياً (أو على وشك الإغلاق)، مُقيَّمةً عبر محرّك ساعات العمل بتوقيت الأردن. وتُستبعَد الأماكن بلا بيانات ساعات. وopenNow=false (أو قيمة فارغة) تُعطّل المرشّح، تماماً كحذفها. يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة openNow: true. ادمجها مع amenities (مثل "مقاهٍ مفتوحة بها مواقف الآن").relevance (افتراضي — المُموَّل ثم التقييم ثم الأحدث)، rating (الأعلى تقييماً أولاً)، name (أ→ي)، newest، أو oldest. تُعيد الاستجابة sort المحلول.0–5). يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة minRating. ادمجها مع بقية المرشّحات — مثل ?minRating=4.5&openNow=true&sort=rating.lat,lng (مثل 31.9539,35.9106). يُضاف لكل نتيجة distanceKm وتُرتَّب القائمة الأقرب أولاً (وهذا يتجاوز sort). ادمجها مع radiusKm وأي مرشّح.near، تُعاد فقط الأماكن ضمن هذا العدد من الكيلومترات. تُعيد الاستجابة near وradiusKm.والأماكن المتساوية في المسافة تحتفظ بترتيب نسبي ثابت عبر الصفحات، فلا يُظهر التنقّل في منطقة
كثيفة المكان نفسه مرتين ولا يتخطّى مكانًا. وهذا أهم مما يبدو: ففي وسط المدينة قد يقع اثنا عشر
مكانًا ضمن عشرة أمتار من نقطة واحدة.الاستجابة
{
"items": [
{
"id": "31c5a4e2-9b1f-4c0a-bb10-2f0c2b8e7a11",
"slug": "ardh-cafe-ard-kafyh-2bd24a",
"nameEn": "Ardh cafe",
"nameAr": "مقهى جوردن بينز",
"logoUrl": "/uploads/places/31c5a4e2-9b1f-4c0a-bb10-2f0c2b8e7a11/logo.webp",
"categories": ["cafe", "food"],
"canonicalCategorySlug": "cafe",
"iconLightUrl": "https://navvo.io/uploads/category-icons/cafe-light.svg",
"iconDarkUrl": "https://navvo.io/uploads/category-icons/cafe-dark.svg",
"center": "POINT(35.913 31.953)",
"contact": { "phone": "+962790000000", "website": "https://example.com" },
"openingHours": { "weekly": { "sunday": "08:00-23:00" } },
"sponsored": false,
"claimStatus": "verified",
"rating": 4.6,
"entrances": [
{ "id": "a1b2c3d4-…", "type": "main", "label": "Main Entrance", "location": "POINT(35.9131 31.9529)" }
]
}
],
"total": 137,
"page": 1,
"limit": 50
}
تتضمّن عناصر القائمة علاقة entrances لكن ليس المراجعات/الصور/الأسئلة. الترتيب دائماً sponsored DESC, rating DESC, createdAt DESC.
صورة المكان
تحمل كل استجابة على شكل مكان صورة واحدة محسومة مسبقًا، كرابط مطلق:
| الحقل | النوع | الوصف |
|---|---|---|
imageUrl | string | null | صورة المكان. رابط مطلق — ضعه مباشرة في <img>. |
imageSource | string | null | المصدر الذي أجاب: logo أو photo أو street. |
imageWidth | number | null | العرض الأصلي بالبكسل. |
imageHeight | number | null | الارتفاع الأصلي بالبكسل. |
تُحسم الصورة على الخادم قبل الطلب، بالترتيب: شعار النشاط ← أفضل صورة معتمدة له (المميّزة أولًا ثم الأقدم) ← لقطة من مستوى الشارع من شبكة تصوير Navvo حيث تتوفّر. ولا تكلّف قراءتها الواجهة شيئًا — فهي عمود في الصف الذي يقرأه الاستعلام أصلًا، ولم يتغيّر زمن استجابة القوائم بإضافتها.
عن مستوى الشارع (imageSource: "street"). حين لا يكون للمكان شعار ولا صورة، تبحث Navvo عن لقطة من مستوى الشارع التقُطت قربه وتعرضها بدلًا من ذلك. ويجدر الانتباه إلى أمرين:
- الأماكن المتجاورة لا تتشارك الصورة نفسها. تُوزَّع اللقطات تنافسيًا لا بشكل مستقل، فيحصل مكانان في الشارع نفسه على صورتين مختلفتين — كما تُقتطع اللقطة الدائرية (360°) باتجاه كل مكان على حدة، فتُنتج اللقطة الواحدة مشهدًا مختلفًا موجّهًا إلى كل واجهة. ولولا ذلك لظهرت لكل متاجر الشارع صورة واحدة متطابقة.
- هي مشهد للموقع لا صورة للنشاط. التُقطت اللقطة من الطريق، عادةً ضمن 60 مترًا، وقد يعود تاريخها إلى سنوات. تجيب عن «كيف يبدو المكان هناك؟» لا عن «كيف تبدو واجهة هذا المتجر؟». تعامل معها كسياق، وفضّل الشعار أو الصورة كلما دلّ
imageSourceعلى وجود أحدهما.
استخدم imageWidth/imageHeight لحجز المساحة قبل وصول الصورة. فصور الأماكن ليست بنسبة أبعاد ثابتة — تتراوح فعليًا بين 224 × 298 و513 × 240 — والشبكة التي لا تحجز مساحة تتراقص بصريًا كلما وصلت صورة.
تكون imageUrl بقيمة null حين لا توجد للمكان صورة من أي نوع؛ اعرض بديلك حينها، وأيقونة الفئة iconLightUrl / iconDarkUrl أدناه خيار جيّد ولا تكون فارغة لأي مكان له فئة.
imageUrlمطلق مثل حقول الأيقونات وبخلاف logoUrl ورابط الصور url النسبيَّين. وهو حقل إضافي منفصل: يحتفظ logoUrl وurl/source في GET /places/thumb بمعناها الحالي دون تغيير.أيقونات تصنيف الأماكن
يحمل كل كائن مكان في الاستجابات الحقلين iconLightUrl و**iconDarkUrl** — أيقونة الدبّوس الخاصة بتصنيف المكان على الخريطة، بصيغة روابط مطلقة (كاملة) يمكنك تمريرها مباشرةً إلى <img> أو إلى icon-image في MapLibre دون الحاجة إلى دمجها مع أي نطاق أساسي.
| الحقل | النوع | ملاحظات |
|---|---|---|
iconLightUrl | string | null | أيقونة الدبّوس لخرائط الأساس الفاتحة. رابط مطلق بصيغة SVG. |
iconDarkUrl | string | null | أيقونة الدبّوس لخرائط الأساس الداكنة. رابط مطلق بصيغة SVG. |
تُشحن نسختان لأنّ أيقونة واحدة تظهر بوضوح ضعيف على إحدى خريطتي الأساس؛ فاختر النسخة وفق النمط الذي تعرضه خريطتك، لا وفق سمة نظام التشغيل.
تُشتقّ الأيقونة من canonicalCategorySlug الخاص بالمكان، ومع تعذّر ذلك من أول عنصر قابل للمطابقة في categories. وتكون قيمة الحقلين null عندما لا يملك المكان تصنيفاً قابلاً للمطابقة — فاعرض عندها دبّوساً بديلاً من عندك بدل افتراض وجود رابط دائماً.
transport يقابله transportation-light.svg، وrestaurants يقابله restaurant-light.svg. وبناء الرابط نصّياً من المعرّف يُنتج روابط تُعيد 404 لنسبة كبيرة من التصنيفات. استخدم دائماً iconLightUrl / iconDarkUrl كما تعيدهما الواجهة، أو ابحث عن المعرّف في GET /map/config ← categories[].logoUrl وروابط الصور url هي مسارات نسبية داخل الموقع (/uploads/…) بخلاف حقلَي الأيقونات؛ فادمجها بنفسك مع https://navvo.io. الحقلان الخاصان بالأيقونات وحدهما مطلقان.GET /places/thumb
صورة مصغّرة خفيفة + حالة مفتوح/مغلق لمكان — مثالية لبطاقات الخريطة.
تُعيد هذه النقطة أيضًا حقول صورة المكان الأربعة. فضّل imageUrl في الأعمال الجديدة: فهو مطلق ويشمل مستوى الشارع. أما url وsource فتبقى كما هي تمامًا للتكاملات القائمة — إذ يبقى source بقيمة logo أو photo أو null ولا يُعيد street أبدًا.
curl -s "https://navvo.io/api/v1/places/thumb?slug=ardh-cafe-ard-kafyh-2bd24a" \
-H "x-api-key: nvvo_YOUR_KEY"
معاملات الاستعلام
الاستجابة
{
"url": "https://navvo.io/uploads/abc123.webp",
"source": "logo",
"rating": 4.6,
"openState": "open",
"openText": "Open · until 23:00"
}
| الحقل | النوع | الوصف |
|---|---|---|
url | string | null | أفضل صورة — الشعار، وإلا صورة مميّزة، وإلا null. |
source | string | null | "logo" أو "photo" أو null. |
rating | number | null | التقييم إذا كان > 0، وإلا null. |
openState | string | null | "open" أو "closing" أو "closed" أو null (محسوبة بتوقيت Asia/Amman). |
openText | string | null | حالة قابلة للقراءة البشرية. |
يُعيد معرّف الرابط غير المعروف 200 مع كل الحقول null (وليس 404).
GET /places/facets
سمات وسائل الراحة المتاحة عبر الأماكن — مُجمَّعة (إمكانية الوصول، المواقف، المدفوعات، وسائل الراحة، خيارات الخدمة، …) مع عدّاد لكل خيار. استخدمها لبناء واجهة "تصفية حسب السمة"، ثم مرّر المعرّفات المختارة إلى GET /places?amenities=. تُحتسب الخيارات المُفعّلة فقط.
| الاستعلام | النوع | ملاحظات |
|---|---|---|
q | string | اختياري — يضيّق العدّادات على الأماكن التي يطابق اسمها |
curl -s "https://navvo.io/api/v1/places/facets" \
-H "x-api-key: $NAVVO_API_KEY"
{
"scanned": 735,
"capped": false,
"groups": [
{ "id": "accessibility", "name": "Accessibility", "total": 201, "options": [
{ "slug": "wheelchair-accessible-entrance", "label": "Wheelchair-accessible entrance", "count": 72 },
{ "slug": "wheelchair-accessible-car-park", "label": "Wheelchair-accessible car park", "count": 63 }
]},
{ "id": "parking", "name": "Parking", "total": 90, "options": [
{ "slug": "free-parking-lot", "label": "Free parking lot", "count": 27 }
]}
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
scanned | number | عدد الأماكن المفحوصة |
capped | boolean | true إذا بلغ الفحص الحدّ الأقصى (توجد أماكن أكثر مما جُمِّع) |
groups[].id / name | string | مجموعة السمة (مثل accessibility) |
groups[].total | number | إجمالي الخيارات المُفعّلة المحتسبة في المجموعة |
groups[].options[] | array | { slug, label, count } مرتّبة حسب العدد — الـ slug هو ما تمرّره إلى ?amenities= |
GET /places/categories
شجرة التصنيفات الكاملة — تسلسل هرمي (أب → أبناء) مع اسم العرض والأيقونة واللون وأعداد الأماكن لكل تصنيف (placeCount = مباشرةً في هذا التصنيف، وtotalPlaces = مُجمَّعاً عبر شجرته الفرعية). استخدمها لبناء واجهة تصفّح/تصفية للتصنيفات؛ وslug أي تصنيف هو ما يحمله المكان في canonicalCategorySlug.
curl -s "https://navvo.io/api/v1/places/categories" \
-H "x-api-key: $NAVVO_API_KEY"
{
"provider": "navvo-places", "count": 500, "rootCount": 38,
"categories": [
{ "slug": "restaurants", "nameEn": "Restaurants", "nameAr": "مطاعم",
"iconName": "restaurant", "color": "#386BFF",
"placeCount": 9, "totalPlaces": 9, "children": [] },
{ "slug": "automotive", "nameEn": "Automotive", "placeCount": 0, "totalPlaces": 0,
"children": [ { "slug": "car-rental", "nameEn": "Car Rental", "placeCount": 0, "totalPlaces": 0, "children": [] } ] }
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
count | number | إجمالي التصنيفات في الشجرة |
rootCount | number | عدد التصنيفات العليا |
categories[] | array | العُقد العليا، كلٌّ منها تكراري بـ children |
…placeCount | number | الأماكن المُسنَدة مباشرةً لهذا التصنيف |
…totalPlaces | number | العدد المُجمَّع شاملاً كل التصنيفات الفرعية |
POST /places/reachable
"ما الذي يمكن الوصول إليه" — كل مكان يمكنك بلوغه من نقطة بدء ضمن ميزانية زمن سير. يحسب Navvo الأيزوكرون (مضلّع إمكانية الوصول) للزمن/الوسيلة، ثم يعيد الأماكن التي تقع داخله — لا ضمن دائرة فحسب، فيحترم شبكة الطرق والممرات الفعلية. وتُطبَّق مرشّحات الاسم/التصنيف/السمات فوق ذلك.
| الحقل | النوع | ملاحظات |
|---|---|---|
origin | {lat,lng} | مطلوب — من أين تبدأ |
minutes | number | ميزانية زمن السير (افتراضي 15؛ أقصى 60) |
costing | string | وسيلة التنقّل: pedestrian (افتراضي)، auto، bicycle |
q | string | مرشّح اسم اختياري |
categories | string | كلمات تصنيف مفصولة بفواصل (اختياري) |
amenities | string | معرّفات سمات مفصولة بفواصل (مطلوبة كلّها، مُفعّلة) |
limit | number | أقصى نتائج (افتراضي 100؛ أقصى 500) |
curl -s -X POST "https://navvo.io/api/v1/places/reachable" \
-H "x-api-key: $NAVVO_API_KEY" -H "content-type: application/json" \
-d '{ "origin": {"lat":31.9539,"lng":35.9106}, "minutes": 15, "costing": "pedestrian", "categories": ["cafe"] }'
{
"provider": "navvo-places", "available": true,
"origin": { "lat": 31.9539, "lng": 35.9106 }, "minutes": 15, "costing": "pedestrian",
"candidatesInBbox": 12, "count": 9,
"reachable": [
{ "id": "…", "slug": "…", "name": "…", "category": "cafe", "rating": 4.6, "lat": 31.95, "lng": 35.91 }
],
"isochrone": { "type": "Polygon", "coordinates": [ /* … */ ] }
}
count هو عدد الأماكن الموجودة فعلاً داخل مضلّع إمكانية الوصول (candidatesInBbox هو المرشّح الأوسع بالصندوق المحيط — وcount ≤ منه). ويُعاد isochrone بصيغة GeoJSON لترسم منطقة الوصول على الخريطة.
GET /places/{slug}
تفاصيل المكان الكاملة: المراجعات والصور المعتمدة، والأسئلة والأجوبة، وarrivalIntelligence.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a" \
-H "x-api-key: nvvo_YOUR_KEY"
معاملات المسار
الاستجابة
بالإضافة إلى كل الحقول من عنصر القائمة، تتضمّن استجابة التفاصيل:
| الحقل | النوع | الوصف |
|---|---|---|
reviews[] | array | المراجعات المعتمدة (rating، body، userId، …). |
photos[] | array | الصور النشطة المعتمدة (url، caption، featured، …). |
questions[] | array | الأسئلة open/answered مع answers[] المتداخلة. تُستبعَد الخيوط المخفيّة إدارياً (status: "hidden")؛ ولا يُعاد إلا المفتوح/المُجاب عنه. |
reports | array | دائماً [] (لا تُكشف أبداً). |
arrivalIntelligence | object | المدخل المُوصى به وإرشادات آخر 50 متراً (انظر أدناه). |
{
"slug": "ardh-cafe-ard-kafyh-2bd24a",
"nameEn": "Ardh cafe",
"center": "POINT(35.913 31.953)",
"rating": 4.6,
"entrances": [ { "id": "a1b2c3d4-…", "type": "main", "location": "POINT(35.9131 31.9529)" } ],
"reviews": [ { "id": "r1…", "rating": 5, "body": "Great coffee.", "status": "approved" } ],
"photos": [ { "id": "p1…", "url": "https://navvo.io/uploads/photo1.webp", "featured": true } ],
"questions": [
{ "id": "q1…", "question": "Is there parking?", "status": "answered",
"answers": [ { "id": "ans1…", "answer": "Yes, in the rear lot.", "ownerVerified": true } ] }
],
"reports": [],
"arrivalIntelligence": {
"version": "navvo-arrival-v1",
"placeCenter": { "lng": 35.913, "lat": 31.953 },
"recommendedEntranceId": "a1b2c3d4-…",
"candidates": [
{
"entranceId": "a1b2c3d4-…",
"type": "main",
"coordinates": { "lng": 35.9131, "lat": 31.9529 },
"distanceFromPlaceMeters": 12,
"score": 99,
"recommended": true,
"lastMetersGuidance": "12 meters from the place pin. Use this as the default arrival point for visitors.",
"routeTarget": { "lat": 31.9529, "lng": 35.9131 }
}
],
"microMap": { "radiusMeters": 50, "title": "Last 50 meters", "guidance": "…" }
}
}
arrivalIntelligence.candidates[0].routeTarget كإحداثية الوجهة لـ التوجيه بدلاً من مركز المكان — فهو يشير إلى المدخل المُوصى به.يُعيد معرّف الرابط (أو المعرّف) غير المعروف 404 Not Found.
الأخطاء
| الحالة | متى |
|---|---|
400 | تحقّق من الصحة (مثل limit > 100). |
401 / 403 | مفتاح مفقود/غير صالح، أو مفتاح يفتقر إلى النطاق places. |
GET /places/{slug}/hours
محرّك ساعات العمل: الجدول الأسبوعي المُطبَّع للمكان، وحالته الحيّة مفتوح/مغلق، والتغيّر التالي للفتح/الإغلاق — كلّها مُقيَّمة بتوقيت الأردن (Asia/Amman) ومراعية للعطل الرسمية.
يقبل Navvo الساعات بصيغتين ويُطبّع كليهما: خريطتنا المنظّمة { "weekly": { "monday": "09:00-17:00", … } }، و نص opening_hours الخام بأسلوب OSM الذي تحمله النقاط المستوردة (مثل "Mo-Fr 09:00-17:00; Sa 10:00-14:00; Su off"، و"24/7"، وفترات الاستراحة مثل "Mo-Fr 08:00-12:00,13:00-18:00"). يعيد المحرّك خريطة weekly قانونية واحدة في كلتا الحالتين.
| الاستعلام | النوع | ملاحظات |
|---|---|---|
at | تاريخ-وقت ISO | اختياري — قيّم عند لحظة مستقبلية (تخطيط الرحلة)؛ الافتراضي الآن |
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/hours" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a",
"hasHours": true,
"at": "2026-06-23T07:30:00.000Z",
"weekly": {
"monday": "09:00-17:00", "tuesday": "09:00-17:00", "wednesday": "09:00-17:00",
"thursday": "09:00-17:00", "friday": "09:00-17:00", "saturday": "10:00-14:00", "sunday": "Closed"
},
"openState": "open",
"openText": "Open · until 17:00",
"nextChange": { "type": "closes", "dayOffset": 0, "weekday": "tuesday", "atMinutes": 1020, "time": "17:00", "label": "Open · closes 17:00" },
"holiday": null
}
| الحقل | النوع | ملاحظات |
|---|---|---|
hasHours | boolean | false (مع حقول null) عندما لا تتوفّر بيانات ساعات للمكان |
weekly | object | null | الجدول القانوني — لكل يوم قائمة HH:MM-HH:MM مفصولة بفواصل، أو "Closed". و"00:00-24:00" = طوال اليوم |
openState | string | null | "open" أو "closing" (≤45 دقيقة على الإغلاق) أو "closed" أو null |
openText | string | null | نص بشري، مثل "Open · until 17:00"، "Closing soon · 17:00". والمكان المفتوح كل يوم 00:00-24:00 (بما في ذلك نص OSM 24/7) يُقرأ "Open 24 hours" مع nextChange: null (ويظلّ تراكب العطلة يضيف تعليقاً، مثل "Open 24 hours · Eid al-Adha"). |
nextChange | object | null | التغيّر التالي خلال 7 أيام: type (opens/closes)، weekday، time، dayOffset (0 = اليوم، 1 = الغد)، وlabel جاهز |
holiday | object | null | عطلة رسمية نشطة (تُحاشى الساعات بـ "… hours may vary"، ولا تُغلَق إغلاقاً قسرياً) |
?at= في المستقبل تُقيّم الجدول لتلك اللحظة — مفيدة لتخطيط "هل سيكون مفتوحاً عند وصولي؟". تُعالَج النطاقات العابرة لمنتصف الليل (18:00-02:00) وتراكبات رمضان/العطل تلقائياً.GET /places/{slug}/popular-times
ازدحام المكان بأسلوب Google: الشبكة الأسبوعية ساعةً بساعة، المستوى الحيّ "الآن"، منحنى اليوم مع ذروته، وأهدأ ساعة عمل ("أفضل وقت للزيارة"). كلّها مُقيَّمة بتوقيت الأردن. الازدحام 0–100.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/popular-times" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a", "hasData": true, "name": "Ardh cafe",
"now": { "day": "Saturday", "hour": 19, "busyness": 50, "label": "A bit busy", "relative": "typical" },
"today": {
"day": "Saturday", "peakHour": 10,
"curve": [18,31,31,43,50,50,43,31,37,43,43,37,31,37,50,75,81,93,75,62,62,43,31,25],
"bestTimeToVisit": { "hour": 5, "busyness": 25, "label": "Not too busy" }
},
"weekly": { "Sunday": [/* 24 قيمة */], "Monday": [/* … */] }
}
| الحقل | النوع | ملاحظات |
|---|---|---|
hasData | boolean | false (مع حقول null) عندما لا يوجد ازدحام للمكان |
now | object | busyness حيّ (0–100) + label بشري + relative (near peak/typical/quieter than usual) |
today.curve | number24 | الازدحام لكل ساعة 0–23 لليوم؛ وpeakHour أكثر ساعاته ازدحاماً |
today.bestTimeToVisit | object | أهدأ ساعة عمل اليوم — { hour, busyness, label } |
weekly | object | مصفوفات ازدحام بـ 24 قيمة لكل يوم (Sunday…Saturday) |
التسميات: Not busy (<25)، Not too busy (<50)، A bit busy (<70)، Busy (<85)، As busy as it gets (≥85).
GET /places/{slug}/attributes
سمات "حول المكان" المنظّمة الكاملة لمكان واحد — كل مجموعة (إمكانية الوصول، المواقف، المدفوعات، خيارات الخدمة، وسائل الراحة، …) مع كل خيار وما إذا كان مُفعّلاً. هذا تفصيل لكل مكان؛ وللسمات على مستوى الكتالوج استخدم GET /places/facets، ولـتصفية الأماكن حسب السمة استخدم ?amenities=.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/attributes" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a", "hasData": true, "name": "Ardh cafe",
"groupCount": 2, "enabledCount": 4,
"groups": [
{ "id": "payments", "name": "Payments", "options": [
{ "slug": "credit-cards", "label": "Credit cards", "enabled": true },
{ "slug": "nfc-mobile-payments", "label": "NFC mobile payments", "enabled": true }
]},
{ "id": "parking", "name": "Parking", "options": [
{ "slug": "on-site-parking", "label": "On-site parking", "enabled": true }
]}
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
hasData | boolean | false (مع groups فارغة) عندما لا يملك المكان سمات منظّمة |
groups[].id / name | string | مجموعة السمة (مثل accessibility) |
groups[].options[] | array | { slug, label, enabled } — وenabled:false تعني أن المكان يفتقر صراحةً لتلك الميزة (مثل "مدخل لذوي الكراسي المتحركة: لا") |
enabledCount | number | إجمالي الخيارات المُفعّلة عبر كل المجموعات |
GET /places/{slug}/reviews-summary
تفصيل التقييم لمكان — المتوسط، إجمالي عدد المراجعات، أكثر مستويات النجوم شيوعاً، والتوزيع الكامل من 5★ إلى 1★ مع الأعداد والنِّسب. مثالي لرسم بياني للتقييمات دون جلب كل مراجعة.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/reviews-summary" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a", "hasData": true, "name": "Ardh cafe",
"average": 4.6, "totalReviews": 128, "mostCommonStars": 5,
"distribution": [
{ "stars": 5, "count": 96, "percent": 75 },
{ "stars": 4, "count": 20, "percent": 15.6 },
{ "stars": 3, "count": 6, "percent": 4.7 },
{ "stars": 2, "count": 3, "percent": 2.3 },
{ "stars": 1, "count": 3, "percent": 2.3 }
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
hasData | boolean | false عندما لا يملك المكان تقييماً/مراجعات |
average | number | null | متوسط تقييم المكان (0–5) |
totalReviews | number | إجمالي عدد المراجعات المحتسبة |
mostCommonStars | number | مستوى النجوم ذو أكثر المراجعات |
distribution[] | array | { stars, count, percent } من 5★ إلى 1★ (نسبة من الإجمالي) |
GET /places/{slug}/photos
معرض الصور لمكان — الصور المعتمَدة والنشطة، المميَّزة أولاً، مع ترقيم صفحات. استخدمه لعرض شريط صور المكان.
| الاستعلام | النوع | ملاحظات |
|---|---|---|
page | number | الصفحة (تبدأ من 1، افتراضي 1) |
limit | number | حجم الصفحة (افتراضي 30، أقصى 100) |
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/photos?limit=20" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a", "name": "Ardh cafe",
"total": 6, "page": 1, "limit": 20, "count": 6,
"photos": [
{ "id": "…", "url": "/uploads/places/…/a59e624b.webp", "caption": "Latest", "featured": true, "mediaType": "image", "createdAt": "2026-05-…" }
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
total | number | إجمالي الصور المعتمَدة للمكان |
count | number | عدد صور هذه الصفحة |
photos[].url | string | رابط الصورة (نسبةً إلى مضيف Navvo) |
photos[].featured | boolean | الصور المميَّزة تُعاد أولاً |
photos[].caption | string | null | تعليق اختياري |
تُعاد فقط الصور المعتمَدة والنشطة (تنطبق المراجعة). والسلج غير المعروف يعيد total: 0 مع مصفوفة photos فارغة.
GET /places/{slug}/nearby
أماكن أخرى قريبة من هذا المكان — قائمة "أماكن مشابهة قريبة" / "المزيد مثله"، مرتّبة حسب المسافة ومُستبعِدةً المكان نفسه. يمكن قصرها على التصنيف نفسه.
| الاستعلام | النوع | ملاحظات |
|---|---|---|
radiusKm | number | نطاق البحث (افتراضي 3، أقصى 50) |
sameCategory | boolean | 1/true → فقط الأماكن التي تشارك تصنيف المكان المرجعي |
limit | number | أقصى نتائج (افتراضي 20، أقصى 100) |
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/nearby?radiusKm=3&sameCategory=1" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a", "center": { "lat": 31.95, "lng": 35.91 },
"radiusKm": 3, "sameCategory": true, "anchorCategory": "cafe", "count": 2,
"places": [
{ "slug": "…", "name": "…", "category": "cafe", "rating": 4.5, "distanceKm": 0.4 }
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
anchorCategory | string | null | تصنيف المكان الذي سألت عنه |
places[].distanceKm | number | المسافة المستقيمة من المكان المرجعي |
GET /places/{slug}/entrances
مداخل/بوابات المكان كقراءة مستقلة خفيفة — الرفيق لمصفوفة entrances[] المضمّنة في تفاصيل المكان، كي يستطيع عميل الملاحة تحديث البوابات دون إعادة جلب المكان كاملاً.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/entrances" \
-H "x-api-key: $NAVVO_API_KEY"
{
"slug": "ardh-cafe-ard-kafyh-2bd24a",
"items": [
{ "id": "a1b2c3d4-…", "type": "main", "label": "Main Entrance", "location": "POINT(35.9131 31.9529)" }
],
"total": 1
}
تكون items فارغة (total: 0) عندما لا توجد مداخل مخصّصة مُهيّأة — وجّه المسار إلى دبوس المكان في هذه الحالة. المعرّف المجهول يعيد 404.
GET /places/{slug}/questions
خيط الأسئلة والأجوبة العام لمكان — الأسئلة مع أجوبتها، الأحدث أولاً. قراءة فقط لمستهلكي الواجهة البرمجية؛ أما طرح الأسئلة والإجابة عنها فميزتان للمستخدمين المسجَّلين.
curl -s "https://navvo.io/api/v1/places/ardh-cafe-ard-kafyh-2bd24a/questions" \
-H "x-api-key: $NAVVO_API_KEY"
[
{
"id": "…", "question": "هل توجد جلسة خارجية؟", "status": "answered", "createdAt": "…",
"answers": [
{ "id": "…", "answer": "نعم — شرفة مظلّلة.", "ownerVerified": true, "createdAt": "…" }
]
}
]
الاستجابة مصفوفة بسيطة (فارغة عندما لا توجد أسئلة). وanswers[].ownerVerified يميّز ردود مالك النشاط التجاري المُوثَّق.
POST /places/suggest
«أضف مكاناً مفقوداً» — دع المستخدمين المسجَّلين يقترحون مكاناً غير موجود على الخريطة بعد. تدخل الاقتراحات قائمة انتظار الإشراف لدى Navvo ويراجعها محرّر؛ لا يظهر شيء على الخريطة قبل الموافقة.
يتطلّب مستخدم Navvo مسجَّل الدخول (Authorization: Bearer <accessToken> — انظر المستخدمون والمزامنة)، وليس مفتاح API.
| الحقل | النوع | ملاحظات |
|---|---|---|
name | string | مطلوب — اسم المكان (بالإنجليزية) |
nameAr | string | الاسم بالعربية (اختياري) |
lat / lng | number | مطلوب — الموقع |
category | string | معرّف تصنيف اختياري (انظر GET /places/categories) |
phone | string | اختياري |
website | string | اختياري |
note | string | اختياري — أي شيء ينبغي أن يعرفه المراجع |
curl -s -X POST "https://navvo.io/api/v1/places/suggest" \
-H "authorization: Bearer $NAVVO_USER_TOKEN" -H "content-type: application/json" \
-d '{ "name": "Abu Ahmad Falafel", "nameAr": "فلافل أبو أحمد", "lat": 31.9539, "lng": 35.9106, "category": "restaurants" }'
{ "ok": true, "id": "…", "status": "quarantined", "note": "Thanks! Your suggestion is queued for review." }
قيمة status عند الاستلام هي دائماً quarantined — يُحتجز الاقتراح للمراجعة. غياب name أو كون lat/lng غير رقمية يعيد 400.
المحتوى الذي يُنشئه المستخدمون
كتابة المراجعات والصور والأسئلة والبلاغات هي ميزة طرف-أول للمستخدم النهائي — تتطلّب تلك النقاط جلسة مستخدم مُسجَّل الدخول (Bearer JWT)، وليس مفتاح API، وهي ليست جزءاً من واجهة التكامل البرمجية. إذا كان منتجك بحاجة إلى محتوى من إنشاء المستخدمين، فتواصل مع Navvo لمناقشة وصول الشركاء.
وللاكتمال، هذه هي نقاط النهاية في تلك العائلة، وكلها تُجيب بـ 401 عند الاكتفاء بمفتاح API:
| نقطة النهاية | الغرض |
|---|---|
POST /places/{slug}/reviews | كتابة مراجعة لمكان. |
POST /places/{slug}/photos | إرفاق صورة مرفوعة مسبقاً بمكان. |
POST /places/upload-photo | رفع ملف الصورة نفسه (multipart) واسترجاع رابطه المخزَّن. |
POST /places/{slug}/questions | طرح سؤال على مكان. |
POST /places/questions/{id}/answers | الإجابة عن سؤال أحدهم. |
POST /places/{slug}/reports | الإبلاغ عن مكان بأنه مغلق أو خاطئ أو مكرّر. |
وتُلخِّص GET /community/reputation/{userId} مكانةَ المساهم الناتجة عن هذا النشاط، وهي عامة: تعيد { "userId", "points", "solved", "level", "events" } حيث level نطاق تقريبي مثل starter. واستدعاؤها برمز Bearer لمعرّفك أنت (أو بصفة مشرف) يضيف سجلّ events المفصّل؛ أما المستدعي المجهول فيحصل على الملخّص فقط.
للمشرفين: استيراد الأماكن بالجملة (CSV)
أدخِل دفعة كبيرة من الأماكن من ملف CSV واحد. هذه المسارات إدارية: فهي تعيش على وحدة تحكّم admin وتتطلّب Bearer JWT لمستخدم Navvo يحمل دور admin أو super_admin. مفتاح API لا يصلح هنا؛ والطلب غير المُصادَق يعيد 401.
هناك وضعان للإدخال: رفع الملف بصيغة multipart/form-data، أو الإشارة إلى ملف موجود سلفاً على الخادم داخل جذر مسموح به. وفي الحالتين يجري التحليل منفصلاً عن طلب HTTP، ويتابع العميل التقدّم بالاستطلاع.
GET /admin/place-imports/files
يسرد ملفات .csv الموجودة في الجذور المسموح بها، الأحدث تعديلاً أولاً — كي تبني واجهة الإدارة منتقي ملفات.
curl -s "https://navvo.io/api/v1/admin/place-imports/files" \
-H "authorization: Bearer $NAVVO_ADMIN_TOKEN"
{
"roots": ["/home/navvo/public_html/data/imports", "/home/navvo/imports"],
"files": [
{ "path": "/home/navvo/public_html/data/imports/places-2026-07.csv",
"name": "places-2026-07.csv", "size": 148203991, "modifiedAt": "2026-07-20T09:12:44.000Z" }
]
}
| الحقل | النوع | ملاحظات |
|---|---|---|
roots | string | الجذور المسموح بها التي جرى فحصها |
files[].path | string | المسار المطلق — هو ما تمرّره إلى /admin/place-imports/from-path |
files[].name / size / modifiedAt | string / number / string | اسم الملف، وحجمه بالبايت، ووقت آخر تعديل بصيغة ISO |
POST /admin/place-imports/upload
ارفع ملف CSV بصيغة multipart/form-data — ملف واحد لكل طلب، ويجب أن ينتهي اسمه بـ .csv وإلا أُعيد 400 "Only .csv files are accepted". يُدفَق الملف مباشرةً إلى القرص باسم مُنقّى، فلا يُحتجَز في الذاكرة مهما كبر.
curl -s -X POST "https://navvo.io/api/v1/admin/place-imports/upload" \
-H "authorization: Bearer $NAVVO_ADMIN_TOKEN" \
-F "[email protected]"
الحدّ الأقصى الافتراضي للحجم 2 غيغابايت (2,147,483,648 بايت)، وهو مفروض في موضعين معاً: حدّ جسم طلب HTTP، وحدّ الملف الواحد في طبقة multipart. وكلاهما قابل للضبط عبر متغيّر البيئة NAVVO_CSV_MAX_BYTES. وإذا بلغ الرفع الحدّ فبُتِر، يُحذَف الملف الجزئي ويُعاد 400 "Upload exceeded the configured size limit".
POST /admin/place-imports/from-path
استورد ملفاً موضوعاً سلفاً على الخادم. الجسم { "path": "/abs/path/file.csv" }.
curl -s -X POST "https://navvo.io/api/v1/admin/place-imports/from-path" \
-H "authorization: Bearer $NAVVO_ADMIN_TOKEN" -H "content-type: application/json" \
-d '{ "path": "/home/navvo/public_html/data/imports/places-2026-07.csv" }'
يُحلَّل المسار ثم يجب أن يقع داخل أحد الجذور المسموح بها — /home/navvo/public_html/data/imports و/home/navvo/imports، مضافاً إليها أي مسارات مطلقة تُدرجها في NAVVO_CSV_IMPORT_DIRS مفصولةً بنقطتين رأسيتين. وهذه القائمة البيضاء هي ما يمنع قراءة ملفات عشوائية من الخادم. فالمسار الواقع خارجها يعيد 400 بالرسالة Path must be inside one of: <roots> (set NAVVO_CSV_IMPORT_DIRS to add more)، والمسار غير الموجود (أو الذي ليس ملفاً) يعيد 400 No such file: <path>.
GET /admin/place-imports/{runId}
استطلع تقدّم عملية استيراد. كلا مسارَي الإدخال يعيدان runId فور إنشاء سجلّ التشغيل.
curl -s "https://navvo.io/api/v1/admin/place-imports/8f2c…" \
-H "authorization: Bearer $NAVVO_ADMIN_TOKEN"
{
"runId": "8f2c…",
"status": "running",
"notes": "Uploaded places-2026-07.csv",
"progress": {
"received": 128500, "staged": 127940, "skipped": 560, "percent": 61,
"bytesRead": 90112000, "bytesTotal": 148203991,
"done": false, "error": null,
"fileName": "1753526400000-places-2026-07.csv",
"startedAt": "2026-07-20T09:15:02.113Z", "finishedAt": null
}
}
| الحقل | النوع | ملاحظات |
|---|---|---|
status | string | حالة التشغيل — تنتقل من running إلى staged عند الاكتمال |
notes | string | null | ملاحظة بشرية عن مصدر الدفعة، وتُلخَّص عند الانتهاء |
progress.received / staged / skipped | number | الصفوف المقروءة، والمُدرَجة في قائمة المراجعة، والمتخطّاة |
progress.percent | number | نسبة الإنجاز 0–100 |
progress.bytesRead / bytesTotal | number | البايتات المقروءة من إجمالي حجم الملف |
progress.done | boolean | true عندما ينتهي التحليل |
progress.error | string | null | رسالة الفشل، إن فشل الاستيراد |
progress.fileName / startedAt / finishedAt | string | null | اسم الملف وطابعا البدء والانتهاء بصيغة ISO |
المعرّف المجهول يعيد 400 "Import run not found".
السلوك
استطلع GET /admin/place-imports/{runId} دورياً واقرأ progress.percent (أو bytesRead مقابل bytesTotal) لرسم شريط التقدّم، وتوقّف عندما تصبح progress.done بقيمة true — أو عندما تصبح progress.error غير null.
staged إلى مسار الحجر ← الموافقة ← النشر نفسه الذي تستخدمه بقية مصادر الاستيراد، تحت المصدر place_csv المُعلَّم بأنه غير موثوق للنشر التلقائي. فلا يظهر أي مكان على الخريطة قبل أن يوافق عليه محرّر.404. استيراد CSV هو المسار المدعوم للإدخال بالجملة.