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

المستخدمون والمزامنة

الحسابات والملفات الشخصية والأماكن المحفوظة والسجل ومشاركة الموقع الحي — واجهة البيانات الشخصية لبناء تطبيق مستهلك بتسجيل دخول على Navvo.

المستخدمون والمزامنة

كل ما يحتاجه تطبيق مستهلك بتسجيل دخول: إنشاء الحساب/تسجيل الدخول، إعدادات الملف الشخصي والخصوصية، إدارة الأجهزة/الجلسات، تصدير البيانات وحذف الحساب (GDPR)، الأماكن المحفوظة والقوائم القابلة للمشاركة، سجل البحث/الزيارات، ومشاركة الموقع الحي مع وقت الوصول المتوقع. هذه هي الواجهة نفسها التي يعمل عليها تطبيق خرائط Navvo.

نموذج المصادقة. بخلاف بقية مرجع الواجهة البرمجية، تعمل هذه النقاط نيابةً عن مستخدم نهائي، لذا تُصادَق بواسطة Bearer JWT الخاص بالمستخدم (Authorization: Bearer <accessToken> من POST /auth/login) — وليس بمفتاح x-api-key. النقاط الموسومة بأنها عامة لا تحتاج أي اعتماد (مفتاح/معرّف المشاركة هو السر).

الحسابات والملف الشخصي

POST /auth/register

إنشاء حساب مستخدم. مجهول الهوية بطبيعته.

الحقلالنوعملاحظات
emailstringمطلوب — بريد إلكتروني صالح، يُخزَّن بأحرف صغيرة
passwordstringمطلوب — 10 أحرف على الأقل
namestringاسم عرض اختياري
turnstileTokenstringخاص بالمتصفح فقط: يخصّ نموذج تسجيل الدخول في موقع Navvo نفسه. عملاء الـ API الذين يستدعون بـ x-api-key لا يخضعون أبداً لاختبار التحقق ويمكنهم إغفال هذا الحقل كلياً
curl -s -X POST "https://navvo.io/api/v1/auth/register" \
  -H "content-type: application/json" \
  -d '{ "email": "[email protected]", "name": "Lina", "password": "superStrongPass123!" }'
{
  "user": { "id": "…", "email": "[email protected]", "name": "Lina", "roles": ["user"] },
  "sessionId": "3f6f2c1e-…",
  "accessToken": "eyJhbGciOi…"
}

accessToken هو Bearer JWT طويل العمر مرتبط بـsessionId — خزّنه بأمان وأرسله مع كل استدعاء خاص بالمستخدم. إبطال الجلسة (تسجيل الخروج، إدارة الأجهزة) يُميت الرمز من جهة الخادم.

POST /auth/login

الجسم نفسه (email، password) والمغلّف نفسه في الاستجابة كما في التسجيل. الحساب المعطَّل أو بيانات الاعتماد الخاطئة تعيد 400. وكما في التسجيل، لا يُطلب اختبار تحقق أبداً عند الاستدعاء بمفتاح API — حقل turnstileToken موجود فقط لنموذج المتصفح الخاص بموقع Navvo.

الحسابات المُنشأة عبر Firebase تسجّل دخولها من هنا أيضًا.الحساب المُنشأ عبر POST /auth/firebase ليس له كلمة مرور في نافّو — فبصمته المخزَّنة عشوائية عمدًا. أما الحسابات المُنشأة بمزوّد البريد وكلمة المرور في Firebase فكلمة مرورها حقيقية لكن Firebase هو من يحفظها، لذا تتحقّق هذه النقطة منها لدى Firebase وتصدر عند النجاح جلسة نافّو المعتادة. ولا يتغيّر شيء بالنسبة للمستدعي: الطلب نفسه والمغلّف نفسه.أما الحسابات المُنشأة بمزوّد اجتماعي (Google وApple…) فلا كلمة مرور لها أصلًا، وتعيد 400 مع code: "PROVIDER_ACCOUNT" مسمّيةً المزوّد والمكان الذي تُبادَل عنده رموزه:
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "This account signs in with Google. …",
  "code": "PROVIDER_ACCOUNT",
  "details": { "signInMethod": "provider", "provider": "google.com", "exchangeEndpoint": "/api/v1/auth/firebase" },
  "requestId": "…"
}
تفرّع على code لا على الرسالة. ولا يُحتسب PROVIDER_ACCOUNT ضمن قفل المحاولات لأنه ليس تخمينًا فاشلًا. أما كلمة المرور الخاطئة فعلًا فتظل تعيد Invalid credentials المعتادة، وتُحتسب.

GET /auth/me

المستخدم المسجَّل دخوله + الجلسة الحالية.

curl -s "https://navvo.io/api/v1/auth/me" -H "authorization: Bearer $NAVVO_USER_TOKEN"
{
  "user": { "id": "…", "email": "[email protected]", "name": "Lina", "roles": ["user"],
            "profile": { "avatar": { "type": "emoji", "value": "🌟" }, "privacy": { "saveHistory": true } } },
  "session": { "id": "…", "sessionId": "…", "lastSeenAt": "…", "expiresAt": "…", "revokedAt": null }
}

PATCH /auth/me

تحديث الملف الشخصي القابل للتعديل. كل الحقول اختيارية؛ لا يتغيّر إلا ما تُرسله.

الحقلالنوعملاحظات
namestringاسم العرض (يُشذَّب، بحد أقصى 160 حرفاً)
avatarobject | null{ type: "image" | "emoji" | "letter", value, bgColor? }؛ وnull يمسح الصورة الرمزية
privacyobject | nullمفاتيح الخصوصية — مفاتيح منطقية ضمن قائمة مسموحة: saveHistory، personalization، shareAnalytics، useLocation؛ وnull يمسح الحزمة
curl -s -X PATCH "https://navvo.io/api/v1/auth/me" \
  -H "authorization: Bearer $NAVVO_USER_TOKEN" -H "content-type: application/json" \
  -d '{ "privacy": { "saveHistory": false, "personalization": true } }'

→ { "ok": true, "user": { … } }. يقرأ العميل الإعدادات مجدداً من GET /auth/me → user.profile.privacy. احترم saveHistory: false بعدم استدعاء POST /me/history.

POST /auth/logout

تسجيل خروج الجلسة الحالية من جهة الخادم (يُبطَل معرّف جلسة الرمز، فيتوقف الرمز عن العمل في كل مكان). → { "ok": true }

GET /auth/sessions · DELETE /auth/sessions

إدارة الأجهزة — اعرض جلسات المستخدم وأبطِلها.

{
  "items": [
    { "sessionId": "…", "current": true, "ipAddress": "94.…", "userAgent": "Mozilla/5.0 …",
      "lastSeenAt": "…", "createdAt": "…", "expiresAt": "…", "revokedAt": null }
  ]
}
  • DELETE /auth/sessions/{sessionId} — إبطال جلسة واحدة → { "ok": true, "revoked": 1 }
  • DELETE /auth/sessions?all=1 — «تسجيل الخروج من كل مكان آخر»: يُبطل كل الجلسات عدا الحالية → { "ok": true, "revoked": n }

GET /auth/me/export

تصدير البيانات بأسلوب GDPR — كل ما تخزّنه Navvo تحت الحساب، في مستند JSON واحد: { exportedAt, account, savedLocations, mapHistory, liveShares, signDisregards, sessions }.

DELETE /auth/me

حذف الحساب ذاتياً (متطلَّب متاجر التطبيقات). يمسح بيانات المستخدم تباعاً — الجلسات، الأماكن المحفوظة والسجل، مشاركات الموقع الحي — ثم يزيل الحساب.

الحقلالنوعملاحظات
confirmstringمطلوب — يجب أن يكون النص الحرفي DELETE
passwordstringمطلوب عندما يملك الحساب كلمة مرور

→ { "deleted": true }

تسجيل الدخول الاجتماعي (OAuth)

المزوّدون يُهيّئهم مشغّل المنصة — اعرض الأزرار دائماً من القائمة الحية:

  • GET /auth/providers (عام) → { "providers": [ { "id": "google", "label": "Google", … } ], "turnstile": { "enabled": true, "siteKey": "…" } }
  • GET /auth/oauth/{provider}/start?next=/map — يعيد توجيه المتصفح (302) إلى صفحة موافقة المزوّد.
  • GET /auth/oauth/{provider}/callback — يعود المزوّد إلى هنا؛ عند النجاح يهبط المتصفح على /map#nvauth=ok&token=<accessToken>. إذا كان البريد يعود لحساب بكلمة مرور، يكون الجزء nvauth=link ويؤكد العميل الربط عبر POST /auth/oauth/link بجسم { linkToken, password? }.

POST /auth/firebase

تبادل تسجيل الدخول عبر Firebase — المسار الموصى به للتطبيقات التي تستخدم Firebase Authentication (بريد/كلمة مرور، Google، Apple، Microsoft، Facebook، GitHub…). سجِّل دخول المستخدم عبر Firebase SDK ثم أرسل رمز المعرّف (ID token)؛ تتحقق Navvo منه مقابل شهادات Google العامة (المُصدِر والجمهور مثبّتان على مشروع Navvo)، وتجد الحساب أو تنشئه عبر البريد الموثَّق، وتعيد مغلّف المصادقة القياسي (user، sessionId، accessToken) — فيحصل المستخدم فوراً على وصول كامل لخدمات الأعضاء (الملاحة، أطلس، الأماكن المحفوظة…).

الحقلالنوعملاحظات
idTokenstringمطلوب — رمز معرّف Firebase من getIdToken()
ربط حساب Navvo موجود مسبقاً يتطلب بريداً موثَّقاً لدى مزوّد المصادقة (مزوّدو OAuth موثَّقون دائماً؛ أما التسجيل ببريد/كلمة مرور فيجب تأكيد البريد أولاً) — لمنع الاستيلاء على الحسابات عبر عناوين غير موثَّقة.

نموذج الوصول: الزائر مقابل المسجَّل

الخريطة الأساسية (البلاطات، الأنماط، التصفح، البحث، تفاصيل الأماكن، قراءات الترميز الجغرافي) مفتوحة. خدمات الأعضاء تتطلب رمز Bearer لمستخدم مسجَّل — أو مفتاح API الخاص بك كمطوّر: التوجيه/الملاحة (POST /routing/*، POST /traffic/route)، وكيل أطلس، الأماكن المحفوظة والقوائم، السجل، المشاركة الحية، التبليغ، ورموز الإشعارات. الاستدعاءات المجهولة لهذه تعيد 401.


رموز الإشعارات (Push)

سجِّل رمز FCM الخاص بالجهاز ليتمكن خادمك (أو Navvo عندما يفعّل المشغّل الإرسال) من الوصول للمستخدم:

  • POST /me/push-tokens — الجسم { "token": "…", "platform": "web" | "android" | "ios" } (مصادقة Bearer). إعادة إرسال رمز موجود تعيد ربطه بالمستخدم الحالي.
  • DELETE /me/push-tokens/{token} — لإلغاء التسجيل عند الخروج.

GET /me/notifications

صندوق إشعارات المستخدم المسجَّل دخوله — كل إشعار أرسلته نافّو له، الأحدث أولًا، ويُسجَّل وقت الإرسال بصرف النظر عن وصول الدفع (push) فعليًا إلى أي جهاز. هذا ما يُبنى عليه سجل الإشعارات داخل التطبيق؛ وهو ليس إيصال تسليم لجهاز بعينه.

curl "https://navvo.io/api/v1/me/notifications?limit=50" \
  -H "authorization: Bearer $TOKEN"

معاملات الاستعلام (اختيارية كلها):

المعاملالنوعملاحظات
limitعدد صحيح١–٢٠٠، والافتراضي ٥٠
offsetعدد صحيح≥ ٠، والافتراضي ٠
typeنصلإرجاع إشعارات نوع واحد فقط — راجع الجدول أدناه

أنواع الإشعارات

type نص حر، لا تعداد ثابت يجب ترميزه مسبقًا — قد تظهر أنواع إشعارات جديدة مع الوقت دون أن يكون ذلك تغييرًا كاسرًا. هذه هي القائمة الكاملة لما ترسله نافّو فعليًا اليوم؛ لا شيء هنا مخطَّط له أو افتراضي:

typeيُرسَل عندحمولة data
commute_leave_nowإطلاق تنبيه وقت مغادرة لرحلة يومية محفوظة — راجع الرحلات اليومية{ "commuteId", "minutesBefore" }
admin_broadcastإرسال المشغّل إعلانًا على مستوى المنصةغير مضبوطة

الاستجابة — صندوق فيه عناصر

{
  "items": [
    {
      "id": "9c3f2e10-…",
      "type": "commute_leave_now",
      "title": "Navvo · حان وقت الانطلاق",
      "body": "غادر خلال ~١٥ دقيقة لـ\"العمل\" — ~٢٢ دقيقة قيادة للوصول بحلول ٠٨:٣٠.",
      "url": "/map?from=31.95,35.91&to=31.96,35.93&navigate=1",
      "data": { "commuteId": "4a1d…", "minutesBefore": 15 },
      "isRead": false,
      "readAt": null,
      "createdAt": "2026-08-27T05:14:02.000Z"
    },
    {
      "id": "7b1a44c2-…",
      "type": "admin_broadcast",
      "title": "نافّو تضيف السعودية",
      "body": "التوجيه والبحث يتوسّعان إلى السعودية خلال الأسابيع القادمة.",
      "url": null,
      "data": null,
      "isRead": true,
      "readAt": "2026-08-26T09:00:11.000Z",
      "createdAt": "2026-08-25T18:30:00.000Z"
    }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}

الاستجابة — صندوق فارغ

مستخدم لم يتلقَّ أي إشعار قط يحصل على مصفوفة فارغة، لا خطأ ولا قيمة فارغة (null):

{ "items": [], "total": 0, "limit": 50, "offset": 0 }

الاستجابة — التصفية بالنوع

GET /me/notifications?type=admin_broadcast تعيد الغلاف نفسه، مقتصرًا على ذلك النوع؛ ويعكس total العدد بعد التصفية لا صندوق الإشعارات كاملًا.

استجابات الخطأ

نفس غلاف الأخطاء في بقية الواجهة. حالتان سترياهما فعليًا في هذه النقطة:

رمز دخول (bearer token) مفقود أو غير صالح — 401:

{
  "message": "Missing bearer token",
  "error": "Unauthorized",
  "statusCode": 401,
  "code": "UNAUTHENTICATED",
  "requestId": "2fa48173-…"
}

limit/offset خارج المدى المسموح — 400:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": ["limit must not be greater than 200"],
  "code": "VALIDATION_ERROR",
  "requestId": "e9289fd5-…"
}

الأماكن المحفوظة

مفضّلات المستخدم باسم مخصص وأيقونة وملاحظة وقائمة ووسوم — تتزامن عبر الأجهزة.

GET · POST /me/saved-places

GET يعيد أحدث 100 من المفضلة. POST يحفظ نقطة:

الحقلالنوعملاحظات
namestringمطلوب — اسم المكان/النقطة
lat / lngnumberمطلوب
detailstringعنوان فرعي/عنوان اختياري
placeIdstringمعرّف مكان Navvo اختياري — يضيف المكان أيضاً إلى قائمة «Saved places» للمستخدم
customNamestringاسم عرض يختاره المستخدم (اختياري)
notestringملاحظة خاصة (اختياري)
iconstringأيقونة/إيموجي (اختياري)
listNamestringتسمية تجميع (اختياري)
tagsstringوسوم مفصولة بفواصل (بحد أقصى 12)
  • PATCH /me/saved-places/{id} — تحديث customName، note، icon، listName، tags.
  • DELETE /me/saved-places/{id} → { "deleted": true, "id" }.

يعود كل مكان محفوظ — من GET، ومن استجابتَي POST/PATCH أيضًا كي تستخدم النتيجة دون إعادة جلب — بهذا الشكل:

{
  "id": "6f1c…", "type": "favorite", "label": "Ardh cafe",
  "lat": 31.990558, "lng": 35.880810,
  "location": { "x": 35.880810, "y": 31.990558 },
  "metadata": {
    "name": "Ardh cafe", "detail": "Rainbow St", "placeId": "31c5a4e2-…",
    "customName": null, "note": null, "icon": null, "listName": null, "tags": []
  },
  "createdAt": "…", "updatedAt": "…"
}
اقرأ الإحداثيات من lat وlng. أما location فهي الهندسة المخزَّنة الخام وشكلها يتبع مشغّل قاعدة البيانات — وهي حاليًا كائن { x, y } حيث x خط الطول وy خط العرض، أي عكس ترتيب lat,lng المعتمد في بقية الواجهة. أما lat وlng فأرقام صريحة تعني ما تقوله دائمًا. تعامل مع location كقيمة مبهمة.

وتوجد حقول المستخدم داخل metadata لا في المستوى الأعلى: customName وnote وicon وlistName وtags جميعها متداخلة هناك. أما label فهو اسم العرض الذي يحافظ عليه الخادم (يتبع customName عند ضبطه، وإلا فيعود إلى name).

GET · POST /me/locations

مراسٍ مثبَّتة مثل المنزل والعمل: { "type": "home" | "work" | "favorite" | "custom", "label", "lat", "lng" }.

يعرضها GET وكلٌّ منها يحمل lat و**lng** كأرقام صريحة إلى جانب هندسة location الخام. اقرأ الأرقام لا الهندسة — انظر التحذير أعلاه.

  • DELETE /me/locations/{id} — حذف مرساة مثبَّتة واحدة. وهي محصورة بالمستخدم المُستدعي؛ فمعرّف مستخدم آخر يعيد 404 لا بيانات ذلك المستخدم.

القوائم — GET · POST /me/lists

مجموعات مسمّاة من أماكن الدليل. لكل حساب أربع قوائم افتراضية تُنشأ تلقائيًا عند أول تعامل مع هذه الواجهة:

kindالاسمالأيقونة
favouritesFavouritesheart
want-to-goWant to gobookmark
saved-tripsSaved Tripsroute
labelledLabelledtag

القائمة الافتراضية قائمة عادية في كل شيء عدا أنها لا تُحذف: يمكن إعادة تسميتها وتغيير أيقونتها ومشاركتها وإخفاؤها عن الخريطة وتفريغها. طابِقها على kind لا على name، فللمستخدم أن يعيد تسمية Favourites كما يشاء.

GET /me/lists

{
  "lists": [
    {
      "id": "9f3c…", "kind": "favourites", "name": "Favourites", "icon": "heart",
      "iconLightUrl": "https://navvo.io/uploads/list-icons/heart-light.svg",
      "iconDarkUrl": "https://navvo.io/uploads/list-icons/heart-dark.svg",
      "system": true, "public": false, "hiddenOnMap": false,
      "shareSlug": "favourites-8a1c4d02", "shareUrl": null,
      "count": 1,
      "canDelete": false,
      "deleteBlockedReason": "A default list cannot be deleted",
      "createdAt": "…", "updatedAt": "…",
      "places": [
        {
          "savedPlaceId": "b71e…", "placeId": "31c5a4e2-…", "slug": "ardh-cafe-ard-kafyh-2bd24a",
          "nameEn": "Ardh cafe", "nameAr": "…", "categories": ["cafe"], "rating": 4.6,
          "lat": 31.953, "lng": 35.913,
          "imageUrl": "https://navvo.io/uploads/places/…/a7f3.webp",
          "imageSource": "photo", "imageWidth": 408, "imageHeight": 544,
          "note": "try the flat white", "addedAt": "…"
        }
      ]
    }
  ],
  "total": 4,
  "totalPlaces": 1
}
الحقلالنوعالوصف
kindstring | nullإحدى القوائم الافتراضية الأربع، أو null لقائمة أنشأها المستخدم.
iconstring | nullاسم الأيقونة. طابِق عليه إن كنت تشحن مجموعة أيقوناتك الخاصة.
iconLightUrl / iconDarkUrlstring | nullروابط مطلقة لملف SVG بمقاس 24 × 24 للأيقونة — ضعها مباشرة في <img>. وتكون كلتاهما null إن لم يكن اسم الأيقونة ضمن ما تشحنه Navvo (انظر أدناه).
systembooleantrue للقائمة الافتراضية.
hiddenOnMapbooleanأخفى المستخدم أماكن هذه القائمة عن خريطته. عرضيّ فقط — الأماكن تُعاد كما هي.
shareUrlstring | nullليس فارغًا إلا بينما public تساوي true.
countnumberعدد الأماكن في القائمة.
canDeletebooleanهل سينجح DELETE الآن.
deleteBlockedReasonstring | nullسبب المنع حين تكون canDelete بقيمة false.
places[].savedPlaceIdstringمعرّف العضوية — وهو ما تأخذه عمليات الحذف والنقل، لا مُعرِّف المكان أو اسمه اللطيف.

تعود القوائم بترتيب ثابت: الأربع الافتراضية أولًا، ثم قوائم المستخدم بحسب الأحدث.

أيقونات القوائم

icon اسم، أما iconLightUrl وiconDarkUrl فملفا SVG جاهزان للعرض له، فلا يحتاج التطبيق الأصلي إلى شحن مكتبة أيقونات ليطابق الويب. اختر النسخة حسب سِمة واجهتك — فالملف الفاتح بخط داكن للخلفيات الفاتحة والعكس بالعكس.

https://navvo.io/uploads/list-icons/{icon}-{light|dark}.svg

يقبل icon أي نص في POST/PATCH، لكن لا تُعاد الروابط إلا للأيقونات التي تشحنها Navvo — وما عداها يُعاد null للحقلين بدل رابط يعطي 404. والقائمة المتاحة:

heart bookmark route tag star house briefcase map-pin flag coffee utensilsshopping-bag store plane car bed mountain trees waves camera music film bookpalette dumbbell gift baby paw-print graduation-cap building-2

والأربع الأولى هي أيقونات القوائم الافتراضية. تعامل مع القائمة على أنها مفتوحة — فقد تُضاف أسماء جديدة — وارجع إلى رمزك الخاص حين تعود الروابط بقيمة null.

اربط واجهتك بـ canDelete وdeleteBlockedReason. فهما يخبرانك بتعطيل زر الحذف وسببه، فلا يكتشف المستخدم القاعدة عبر استجابة 409.

بقية النقاط

  • POST /me/lists { "name": "Weekend cafés", "public"?: false, "icon"?: "coffee" } — إنشاء قائمة. تُعيد الشكل نفسه الوارد في GET.
  • PATCH /me/lists/{id} { "name"?, "public"?, "hiddenOnMap"?, "icon"? } — تعديل أي قائمة، افتراضية كانت أم لا.
  • DELETE /me/lists/{id} — حذف قائمة. يُعاد 409 إن كانت افتراضية أو ما زالت تحوي أماكن — فرّغها أولًا. → { "deleted": true, "id" }
  • POST /me/lists/{id}/places { "placeId": "…", "note"? } — إضافة مكان من الدليل. وإعادة إضافة مكان موجود تُحدّث ملاحظته بدل تكراره.
  • DELETE /me/lists/{id}/places/{savedPlaceId} — إزالة مكان واحد من قائمة.
  • PATCH /me/lists/{id}/places/{savedPlaceId} { "toListId": "…" } — نقل مكان إلى قائمة أخرى. نقل حقيقي، فتنتقل الملاحظة معه ولا يمرّ المكان بلحظة لا ينتمي فيها إلى أي قائمة. وإن كانت الوجهة تحويه أصلًا فيُدمجان (mergedWithExisting: true) بدل التكرار. → { "moved": true, "savedPlaceId", "fromListId", "toListId", "mergedWithExisting" }
  • POST /me/lists/{id}/feedback { "message": "…", "category"? } — إرسال ملاحظات حول قائمة. → { "ok": true, "id" }

GET /shared-lists/{shareSlug} — عام

عارض القوائم المشتركة — دون مصادقة؛ المعرّف هو السر، ولا تُحلّ إلا القوائم التي وسمها المالك بأنها public. الاستجابة منقّاة (لا معرّفات مستخدمين):

curl -s "https://navvo.io/api/v1/shared-lists/weekend-cafes-a1b2c3"
{
  "found": true, "name": "مقاهي نهاية الأسبوع", "updatedAt": "…",
  "places": [
    { "slug": "ardh-cafe-ard-kafyh-2bd24a", "nameEn": "Ardh cafe", "nameAr": "…",
      "categories": ["cafe"], "rating": 4.6, "center": "POINT(35.913 31.953)", "note": null }
  ]
}

المعرّف المجهول/الخاص يعيد { "found": false }.

عرض المالك. تعيد GET /me/shared-lists/{slug} القائمة نفسها لمالكها، برمز Bearer بدل الاعتماد على سرّية المُعرِّف. وهي تُجيب بـ 401 دون رمز، لذا استخدم الصيغة العامة /shared-lists/{slug} لروابط المشاركة، وهذه داخل واجهة حساب موثَّقة.

GET · POST · DELETE /me/history

سجل بحث/زيارات المستخدم (الأحدث أولاً، ويُعاد 100 كحد أقصى).

  • GET /me/history → { "items": [ … ], "total" }
  • POST /me/history بجسم { "name", "lat", "lng", "detail"?, "placeId"? } — تسجيل مُدخَل
  • DELETE /me/history — مسح الكل → { "deleted": true }
  • DELETE /me/history/{id} — إزالة مُدخَل واحد

الرحلات اليومية (وقت المغادرة + التنبيهات)

رحلات محفوظة (منزل→عمل ونحوها)، لكل منها أيام تكرار، وعمر دائم أو مؤقّت، وأي عدد من أوقات التنبيه قبل المغادرة. يقيّم الخادم كل رحلة مُفعّلة كل دقيقة وفق حركة المرور الحية، وعند اقتراب وقت المغادرة يرسل إشعاراً (عبر رموز الإشعارات المسجّلة للمستخدم) — مثل «غادر خلال ~15 دقيقة إلى العمل».

كل المسارات تتطلب مصادقة Bearer ومقيّدة بالمستخدم المسجّل.

  • GET /me/commutes → { "commutes": Commute[] }
  • POST /me/commutes بجسم Commute (بدون id) → { "commute" } (حد أقصى 20 لكل مستخدم)
  • PUT /me/commutes/{id} (جسم Commute جزئي) → { "commute" }
  • DELETE /me/commutes/{id} → { "ok": true }
{
  "id": "…",
  "name": "العمل",
  "from": { "lat": 31.95, "lng": 35.91, "label": "المنزل" },
  "to":   { "lat": 31.99, "lng": 35.95, "label": "المكتب" },
  "arriveBy": "09:00",
  "days": [0,1,2,3,4],
  "permanent": true,
  "expiresAt": null,
  "notifications": [{ "minutesBefore": 30 }, { "minutesBefore": 15 }],
  "enabled": true
}
يستخدم تسليم التنبيهات خدمة Firebase Cloud Messaging ويُفعَّل بعد أن يضبط مشغّل المنصة بيانات اعتماد FCM على الخادم؛ أما سجلّات الرحلات والجدولة فتُخزَّن في كل الأحوال. تُحسب الأوقات بتوقيت Asia/Amman.

مشاركة الموقع الحي

شارك موقعاً حياً تحت مفتاح متجدد يستحيل تخمينه. المشاركة تنتهي تلقائياً بعد ساعتين من آخر إشارة (ping)؛ وكل إشارة تمدّدها.

POST /me/live-share

بدء مشاركة: { "label"? } → { "shareKey": "…", "active": true, "expiresAt": "…" }. رابط العارض الذي تشاركه هو https://navvo.io/map?live=<shareKey>.

GET /me/live-share

مشاركات المستخدم النشطة: { "items": [ { "shareKey", "label", "lastPingAt", "expiresAt" } ] }.

POST /me/live-share/{key}/ping

ادفع الموقع الحالي (استدعِه كل بضع ثوانٍ أثناء المشاركة):

الحقلالنوعملاحظات
lat / lngnumberمطلوب
headingnumberالاتجاه بالدرجات (اختياري)
accuracynumberدقة GPS بالأمتار (اختياري)
destNamestringمشاركة وقت الوصول (Share-ETA) — اسم الوجهة أثناء الملاحة
destLat / destLngnumberShare-ETA — إحداثيات الوجهة
etaSecondsnumberShare-ETA — الثواني حتى الوصول؛ أعد إرسالها مع كل إشارة أثناء الملاحة

→ { "active": true } (و{ "active": false } بعد الإيقاف/الانتهاء — توقف عندها عن الإرسال).

POST /me/live-share/{key}/stop

إنهاء المشاركة → { "stopped": true }.

GET /live/{key} — عام

قراءة العارض — دون مصادقة، استعلم عنها بضع مرات في الدقيقة:

{
  "active": true, "lat": 31.9539, "lng": 35.9106, "heading": 270, "accuracy": 8,
  "label": "Lina", "updatedAt": "…",
  "destination": { "name": "المطار", "lat": 31.7226, "lng": 35.9932 },
  "etaSeconds": 1240, "arrivalTime": "2026-07-09T11:42:00.000Z"
}

تظهر destination وetaSeconds وarrivalTime فقط أثناء ملاحة المُشارِك (أي عندما يرسل حقول Share-ETA مع الإشارة) — وإلا فهي null. المفاتيح المنتهية/المجهولة تعيد { "active": false }؛ والمشاركة التي بدأت ولم تُرسل إشارة بعد تعيد { "active": true, "pending": true }.


روابط عميقة إلى خريطة Navvo

سلّم المستخدم من تطبيقك إلى تجربة خريطة Navvo الكاملة بروابط بسيطة:

الرابطيفتح
https://navvo.io/map/place/{slug}صفحة تفاصيل مكان من الفهرس
https://navvo.io/map?pin=31.95,35.91&name=Meet%20hereدبوساً موضوعاً عند lat,lng مع تسمية اختيارية
https://navvo.io/map?from=31.95,35.91&to=31.72,35.99&navigate=1&mode=autoمساراً — ومع navigate=1 تبدأ الملاحة خطوة بخطوة فوراً؛ وmode هو ملف توجيه (auto، pedestrian، bicycle، …)
https://navvo.io/map?live=KEYعارض المشاركة الحية الذي يتتبع مفتاح مشاركة

الإشعارات الفورية لا تديرها Navvo. لا توجد خدمة إشعارات مستضافة لدى Navvo — على تطبيقات الجوال إطلاق إشعارات FCM/APNS الخاصة بها من خادمها هي، مستخدمةً هذه الواجهات كمصدر بيانات (مثل الاستعلام عن GET /live/{key} لرصد الوصول، أو منطقك الخاص فوق الأماكن المحفوظة والسجل).
Copyright © 2026