Navvo
مرجع الواجهة البرمجية

الأماكن

قوائم الأماكن وتفاصيل غنية عن المكان — الفئات وساعات العمل والصور والتقييمات وإرشادات الوصول.

الأماكن

اعرض قوائم الأماكن واجلب تفاصيل غنية لمكان واحد، بما في ذلك المراجعات والصور والأسئلة والأجوبة وذكاء الوصول (المدخل المُوصى به وإرشادات آخر 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"

معاملات الاستعلام

q
string
مطابقة جزئية غير حساسة لحالة الأحرف مقابل nameEn أو nameAr.
page
number
رقم الصفحة بدءاً من 1. القيمة الافتراضية 1 (الحدّ الأدنى 1).
limit
number
حجم الصفحة. القيمة الافتراضية 50، والحدّ الأقصى 100.
sponsored
boolean
تصفية إلى الأماكن المُموَّلة (أو غير المُموَّلة). تُحلَّل القيم الصحيحة (true/1/yes/on) والزائفة (false/0/no/off) بشكل صحيح — فـ sponsored=false تعيد فعلاً الأماكن غير المُموَّلة فقط. والقيمة الفارغة أو غير المعروفة تتصرّف كمعامل محذوف (بلا تصفية).
category
string
تصفية إلى تصنيف واحد — يطابق canonicalCategorySlug للمكان أو أي عنصر في مصفوفة categories لديه. اكتشف المعرّفات عبر GET /places/categories. عند ضبطها يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة category.
amenities
string
معرّفات سمات (facet slugs) مفصولة بفواصل (مثل wheelchair-accessible-entrance,free-parking-lot). يُطابَق المكان فقط إذا كانت كل السمات المطلوبة موجودة ومُفعّلة. قد يكون المعرّف خياراً (wi-fi) أو مجموعة كاملة (parking = يملك أي موقف). عند ضبطها يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة مصفوفة amenities المُطبّعة. اكتشف المعرّفات المتاحة عبر GET /places/facets.
openNow
boolean
عند true، تُعاد فقط الأماكن المفتوحة حالياً (أو على وشك الإغلاق)، مُقيَّمةً عبر محرّك ساعات العمل بتوقيت الأردن. وتُستبعَد الأماكن بلا بيانات ساعات. وopenNow=false (أو قيمة فارغة) تُعطّل المرشّح، تماماً كحذفها. يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة openNow: true. ادمجها مع amenities (مثل "مقاهٍ مفتوحة بها مواقف الآن").
sort
string
ترتيب النتائج: relevance (افتراضي — المُموَّل ثم التقييم ثم الأحدث)، rating (الأعلى تقييماً أولاً)، name (أ→ي)، newest، أو oldest. تُعيد الاستجابة sort المحلول.
minRating
number
فقط الأماكن التي تقييمها ≥ هذه القيمة (0–5). يعكس total المجموعة المُصفّاة وتُرجِع الاستجابة minRating. ادمجها مع بقية المرشّحات — مثل ?minRating=4.5&openNow=true&sort=rating.
near
string
"بالقرب مني" — نقطة بداية lat,lng (مثل 31.9539,35.9106). يُضاف لكل نتيجة distanceKm وتُرتَّب القائمة الأقرب أولاً (وهذا يتجاوز sort). ادمجها مع radiusKm وأي مرشّح.
radiusKm
number
مع 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.

صورة المكان

تحمل كل استجابة على شكل مكان صورة واحدة محسومة مسبقًا، كرابط مطلق:

الحقلالنوعالوصف
imageUrlstring | nullصورة المكان. رابط مطلق — ضعه مباشرة في <img>.
imageSourcestring | nullالمصدر الذي أجاب: logo أو photo أو street.
imageWidthnumber | nullالعرض الأصلي بالبكسل.
imageHeightnumber | 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 دون الحاجة إلى دمجها مع أي نطاق أساسي.

الحقلالنوعملاحظات
iconLightUrlstring | nullأيقونة الدبّوس لخرائط الأساس الفاتحة. رابط مطلق بصيغة SVG.
iconDarkUrlstring | nullأيقونة الدبّوس لخرائط الأساس الداكنة. رابط مطلق بصيغة SVG.

تُشحن نسختان لأنّ أيقونة واحدة تظهر بوضوح ضعيف على إحدى خريطتي الأساس؛ فاختر النسخة وفق النمط الذي تعرضه خريطتك، لا وفق سمة نظام التشغيل.

تُشتقّ الأيقونة من canonicalCategorySlug الخاص بالمكان، ومع تعذّر ذلك من أول عنصر قابل للمطابقة في categories. وتكون قيمة الحقلين null عندما لا يملك المكان تصنيفاً قابلاً للمطابقة — فاعرض عندها دبّوساً بديلاً من عندك بدل افتراض وجود رابط دائماً.

لا تُركّب هذه الروابط بنفسك انطلاقاً من معرّف التصنيف (slug). فالمعرّف ليس اسم الملف: 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"

معاملات الاستعلام

slug
string required
معرّف رابط المكان.

الاستجابة

{
  "url": "https://navvo.io/uploads/abc123.webp",
  "source": "logo",
  "rating": 4.6,
  "openState": "open",
  "openText": "Open · until 23:00"
}
الحقلالنوعالوصف
urlstring | nullأفضل صورة — الشعار، وإلا صورة مميّزة، وإلا null.
sourcestring | null"logo" أو "photo" أو null.
ratingnumber | nullالتقييم إذا كان > 0، وإلا null.
openStatestring | null"open" أو "closing" أو "closed" أو null (محسوبة بتوقيت Asia/Amman).
openTextstring | nullحالة قابلة للقراءة البشرية.

يُعيد معرّف الرابط غير المعروف 200 مع كل الحقول null (وليس 404).


GET /places/facets

سمات وسائل الراحة المتاحة عبر الأماكن — مُجمَّعة (إمكانية الوصول، المواقف، المدفوعات، وسائل الراحة، خيارات الخدمة، …) مع عدّاد لكل خيار. استخدمها لبناء واجهة "تصفية حسب السمة"، ثم مرّر المعرّفات المختارة إلى GET /places?amenities=. تُحتسب الخيارات المُفعّلة فقط.

الاستعلامالنوعملاحظات
qstringاختياري — يضيّق العدّادات على الأماكن التي يطابق اسمها
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 }
    ]}
  ]
}
الحقلالنوعملاحظات
scannednumberعدد الأماكن المفحوصة
cappedbooleantrue إذا بلغ الفحص الحدّ الأقصى (توجد أماكن أكثر مما جُمِّع)
groups[].id / namestringمجموعة السمة (مثل accessibility)
groups[].totalnumberإجمالي الخيارات المُفعّلة المحتسبة في المجموعة
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": [] } ] }
  ]
}
الحقلالنوعملاحظات
countnumberإجمالي التصنيفات في الشجرة
rootCountnumberعدد التصنيفات العليا
categories[]arrayالعُقد العليا، كلٌّ منها تكراري بـ children
…placeCountnumberالأماكن المُسنَدة مباشرةً لهذا التصنيف
…totalPlacesnumberالعدد المُجمَّع شاملاً كل التصنيفات الفرعية

POST /places/reachable

"ما الذي يمكن الوصول إليه" — كل مكان يمكنك بلوغه من نقطة بدء ضمن ميزانية زمن سير. يحسب Navvo الأيزوكرون (مضلّع إمكانية الوصول) للزمن/الوسيلة، ثم يعيد الأماكن التي تقع داخله — لا ضمن دائرة فحسب، فيحترم شبكة الطرق والممرات الفعلية. وتُطبَّق مرشّحات الاسم/التصنيف/السمات فوق ذلك.

الحقلالنوعملاحظات
origin{lat,lng}مطلوب — من أين تبدأ
minutesnumberميزانية زمن السير (افتراضي 15؛ أقصى 60)
costingstringوسيلة التنقّل: pedestrian (افتراضي)، auto، bicycle
qstringمرشّح اسم اختياري
categoriesstringكلمات تصنيف مفصولة بفواصل (اختياري)
amenitiesstringمعرّفات سمات مفصولة بفواصل (مطلوبة كلّها، مُفعّلة)
limitnumberأقصى نتائج (افتراضي 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"

معاملات المسار

slug
string required
معرّف رابط المكان الفريد.

الاستجابة

بالإضافة إلى كل الحقول من عنصر القائمة، تتضمّن استجابة التفاصيل:

الحقلالنوعالوصف
reviews[]arrayالمراجعات المعتمدة (rating، body، userId، …).
photos[]arrayالصور النشطة المعتمدة (url، caption، featured، …).
questions[]arrayالأسئلة open/answered مع answers[] المتداخلة. تُستبعَد الخيوط المخفيّة إدارياً (status: "hidden")؛ ولا يُعاد إلا المفتوح/المُجاب عنه.
reportsarrayدائماً [] (لا تُكشف أبداً).
arrivalIntelligenceobjectالمدخل المُوصى به وإرشادات آخر 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": "…" }
  }
}
ذكاء الوصول هو ما يجعل Navvo مفيداً للتوصيل وطلب المركبات. استخدم 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
}
الحقلالنوعملاحظات
hasHoursbooleanfalse (مع حقول null) عندما لا تتوفّر بيانات ساعات للمكان
weeklyobject | nullالجدول القانوني — لكل يوم قائمة HH:MM-HH:MM مفصولة بفواصل، أو "Closed". و"00:00-24:00" = طوال اليوم
openStatestring | null"open" أو "closing" (≤45 دقيقة على الإغلاق) أو "closed" أو null
openTextstring | 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").
nextChangeobject | nullالتغيّر التالي خلال 7 أيام: type (opens/closes)، weekday، time، dayOffset (0 = اليوم، 1 = الغد)، وlabel جاهز
holidayobject | 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": [/* … */] }
}
الحقلالنوعملاحظات
hasDatabooleanfalse (مع حقول null) عندما لا يوجد ازدحام للمكان
nowobjectbusyness حيّ (0–100) + label بشري + relative (near peak/typical/quieter than usual)
today.curvenumber24الازدحام لكل ساعة 0–23 لليوم؛ وpeakHour أكثر ساعاته ازدحاماً
today.bestTimeToVisitobjectأهدأ ساعة عمل اليوم — { hour, busyness, label }
weeklyobjectمصفوفات ازدحام بـ 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 }
    ]}
  ]
}
الحقلالنوعملاحظات
hasDatabooleanfalse (مع groups فارغة) عندما لا يملك المكان سمات منظّمة
groups[].id / namestringمجموعة السمة (مثل accessibility)
groups[].options[]array{ slug, label, enabled } — وenabled:false تعني أن المكان يفتقر صراحةً لتلك الميزة (مثل "مدخل لذوي الكراسي المتحركة: لا")
enabledCountnumberإجمالي الخيارات المُفعّلة عبر كل المجموعات

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 }
  ]
}
الحقلالنوعملاحظات
hasDatabooleanfalse عندما لا يملك المكان تقييماً/مراجعات
averagenumber | nullمتوسط تقييم المكان (0–5)
totalReviewsnumberإجمالي عدد المراجعات المحتسبة
mostCommonStarsnumberمستوى النجوم ذو أكثر المراجعات
distribution[]array{ stars, count, percent } من 5★ إلى 1★ (نسبة من الإجمالي)

GET /places/{slug}/photos

معرض الصور لمكان — الصور المعتمَدة والنشطة، المميَّزة أولاً، مع ترقيم صفحات. استخدمه لعرض شريط صور المكان.

الاستعلامالنوعملاحظات
pagenumberالصفحة (تبدأ من 1، افتراضي 1)
limitnumberحجم الصفحة (افتراضي 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-…" }
  ]
}
الحقلالنوعملاحظات
totalnumberإجمالي الصور المعتمَدة للمكان
countnumberعدد صور هذه الصفحة
photos[].urlstringرابط الصورة (نسبةً إلى مضيف Navvo)
photos[].featuredbooleanالصور المميَّزة تُعاد أولاً
photos[].captionstring | nullتعليق اختياري

تُعاد فقط الصور المعتمَدة والنشطة (تنطبق المراجعة). والسلج غير المعروف يعيد total: 0 مع مصفوفة photos فارغة.


GET /places/{slug}/nearby

أماكن أخرى قريبة من هذا المكان — قائمة "أماكن مشابهة قريبة" / "المزيد مثله"، مرتّبة حسب المسافة ومُستبعِدةً المكان نفسه. يمكن قصرها على التصنيف نفسه.

الاستعلامالنوعملاحظات
radiusKmnumberنطاق البحث (افتراضي 3، أقصى 50)
sameCategoryboolean1/true → فقط الأماكن التي تشارك تصنيف المكان المرجعي
limitnumberأقصى نتائج (افتراضي 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 }
  ]
}
الحقلالنوعملاحظات
anchorCategorystring | nullتصنيف المكان الذي سألت عنه
places[].distanceKmnumberالمسافة المستقيمة من المكان المرجعي

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.

الحقلالنوعملاحظات
namestringمطلوب — اسم المكان (بالإنجليزية)
nameArstringالاسم بالعربية (اختياري)
lat / lngnumberمطلوب — الموقع
categorystringمعرّف تصنيف اختياري (انظر GET /places/categories)
phonestringاختياري
websitestringاختياري
notestringاختياري — أي شيء ينبغي أن يعرفه المراجع
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" }
  ]
}
الحقلالنوعملاحظات
rootsstringالجذور المسموح بها التي جرى فحصها
files[].pathstringالمسار المطلق — هو ما تمرّره إلى /admin/place-imports/from-path
files[].name / size / modifiedAtstring / 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
  }
}
الحقلالنوعملاحظات
statusstringحالة التشغيل — تنتقل من running إلى staged عند الاكتمال
notesstring | nullملاحظة بشرية عن مصدر الدفعة، وتُلخَّص عند الانتهاء
progress.received / staged / skippednumberالصفوف المقروءة، والمُدرَجة في قائمة المراجعة، والمتخطّاة
progress.percentnumberنسبة الإنجاز 0–100
progress.bytesRead / bytesTotalnumberالبايتات المقروءة من إجمالي حجم الملف
progress.donebooleantrue عندما ينتهي التحليل
progress.errorstring | nullرسالة الفشل، إن فشل الاستيراد
progress.fileName / startedAt / finishedAtstring | nullاسم الملف وطابعا البدء والانتهاء بصيغة ISO

المعرّف المجهول يعيد 400 "Import run not found".

السلوك

التحليل منفصل عن الطلب. ينشئ كلا مسارَي الإدخال سجلّ التشغيل ثم يُطلقان التحليل المتدفّق دون انتظاره، فيعود طلب HTTP فور وجود السجلّ. لذلك لا مهلة طلب ولا مهلة وسيط عكسي تحدّ من مدة الاستيراد — مهلتا الطلب والاتصال معطّلتان على الخادم، وزمن الإبقاء على الاتصال (keep-alive) 15 دقيقة. عامِل الاستجابة بوصفها إيصالاً، لا نتيجة.

استطلع GET /admin/place-imports/{runId} دورياً واقرأ progress.percent (أو bytesRead مقابل bytesTotal) لرسم شريط التقدّم، وتوقّف عندما تصبح progress.done بقيمة true — أو عندما تصبح progress.error غير null.

لا شيء يُنشَر تلقائياً. تدخل السجلّات المستوردة بحالة staged إلى مسار الحجر ← الموافقة ← النشر نفسه الذي تستخدمه بقية مصادر الاستيراد، تحت المصدر place_csv المُعلَّم بأنه غير موثوق للنشر التلقائي. فلا يظهر أي مكان على الخريطة قبل أن يوافق عليه محرّر.
نقاط الاستيراد السابقة القائمة على الكشط الآلي أُزيلت من الواجهة البرمجية، واستدعاؤها اليوم يعيد 404. استيراد CSV هو المسار المدعوم للإدخال بالجملة.
Copyright © 2026