صقر

اسأل، أو دعنا نخبرك، أو خذ كل شيء.

لبيانات الأسطول ثلاث طرق تصل بها إلى أنظمتك، ومعظم التكاملات التي تفشل اختارت الطريقة الخطأ: خريطة مباشرة مبنية على استعلام كل مركبة على حدة، أو مستودع بيانات يغذيه webhooks فيفقد يوما كلما أعيد تشغيل المستقبل. وهنا كيف تختار، والحساب الذي وراء الاختيار.

REST API

المواقع والرحلات والأحداث والأصول، موثقة، بترقيم صفحات بالمؤشر وحد لمعدل الطلبات.

webhooks

موقع، يعاد إرساله مع تباعد زمني، وقابل للإعادة لمدة 72 ساعة.

ERP والتشغيل

أرسل المهام إلى النظام، واسحب منه إثبات التسليم ومعدل الاستخدام.

تصدير البيانات

ملفات CSV أو Parquet مجدولة إلى مساحة تخزين تملكها. بياناتك تبقى لك.

أيها يناسب أسطولك

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

400
ما مدى حداثة البيانات التي تحتاجها؟
الاستخدام

اسحب التدفق بمؤشر

طلب واحد يعيد كل ما تغير منذ الطلب السابق، عبر الأسطول كله. سهل البناء، سهل التصحيح، ولا شيء يضيع بين الطلبات.

طلب كل مركبة على حدة400/دقيقة · 67% من الحد
تدفق واحد بمؤشر، 1,000 صف في الصفحة2.0/دقيقة · 0.33% من الحد

حد المعدل: 600 طلب في الدقيقة لكل مفتاح

أحداث webhook يوميا
13,600
تصدير يومي
1,440,000 صف · 20 ميجابايت

كيف تبدو الطلبات

اسأل: تدفق المؤشر

طلب واحد، والأسطول كله، وكل شيء منذ المرة الماضية.

GET https://api.nml.sa/fleet/v1/positions?since=cur_7Hd91K&limit=1000

200 OK
{ "data": [ …up to 1000 rows… ],
  "next": "cur_7Hd9Qx",     // pass this as ?since next time
  "has_more": false }        // true: call again straight away

أخبرنا: ادفع مهمة

أعد المحاولة بلا قلق، فمفتاح Idempotency-Key يعني أن المهمة تنشأ مرة واحدة.

POST https://api.nml.sa/fleet/v1/jobs
Idempotency-Key: ERP-SO-448213

{ "ref": "SO-448213", "vehicle": "VAN-042",
  "stop": { "position": [21.5433, 39.1728], "window": ["10:00", "12:00"] } }

لتصلك البيانات: webhook

موقع، مع حفظ وقت التسجيل ووقت الاستلام معا.

{
  "id": "evt_01J8Q4Z6K3",
  "type": "trip.completed",
  "vehicle": {
    "id": "TRK-114",
    "plate": "4821 RSA"
  },
  "trip": {
    "started_at": "2026-09-14T05:12:40+03:00",
    "ended_at": "2026-09-14T08:47:10+03:00",
    "km": 212.4,
    "driver_id": "DRV-3107"
  },
  "last_position": {
    "position": [
      24.7136,
      46.6753
    ],
    "speed": 0,
    "ignition": false,
    "recorded_at": "2026-09-14T08:47:10+03:00",
    "received_at": "2026-09-14T08:47:12+03:00"
  }
}

العنوان الأساسي · https://api.nml.sa/fleet/v1

حين يتوقف endpoint لديك طوال نهاية الأسبوع

يعاد إرسال التسليم الفاشل 9 مرات، والفاصل بين المحاولات يطول في كل مرة، وآخرها بعد 46.7 h من أول إخفاق. ويبقى كل شيء قابلا لإعادة الإرسال 72 ساعة.

إعادة المحاولة وحدها لا تغطي انقطاعا مدته 48 ساعة في عطلة نهاية الأسبوع، إذ تقف على بعد 1.3 ساعة منه. أما نافذة إعادة الإرسال فتغطيه وتزيد 24 ساعة: صباح الأحد تعيد الإرسال من ليلة الخميس فلا يضيع شيء.

المستقبل متوقف 48 ساعة تنتهي الإعادة · 72 ساعة
  1. #1 · 30 ثانية
  2. #2 · 3 دقائق
  3. #3 · 13 دقيقة
  4. #4 · 43 دقيقة
  5. #5 · 1.7 h
  6. #6 · 4.7 h
  7. #7 · 10.7 h
  8. #8 · 22.7 h
  9. #9 · 46.7 h

تحقق من أنه جاء منا

HMAC قياسي، بلا حاجة إلى مكتبة. ارفض الطوابع الزمنية القديمة، وقارن في زمن ثابت، فلا يعاد إرسال طلب ملتقط إليك.

// Every webhook carries: Saqr-Signature: t=<unix seconds>,v1=<hex>
// v1 is HMAC-SHA256 of "<t>.<raw body>" with your endpoint secret.
async function verify(raw: string, header: string, secret: string) {
  const { t, v1 } = Object.fromEntries(
    header.split(",").map((p) => p.split("=") as [string, string])
  );
  // Reject anything older than five minutes: a captured request replayed later.
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;

  const key = await crypto.subtle.importKey(
    "raw", new TextEncoder().encode(secret),
    { name: "HMAC", hash: "SHA-256" }, false, ["sign"]
  );
  const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${t}.${raw}`));
  const hex = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
  // Compare every character, so the time taken says nothing about where they differ.
  let diff = hex.length ^ v1.length;
  for (let i = 0; i < hex.length; i++) diff |= hex.charCodeAt(i) ^ (v1.charCodeAt(i) || 0);
  return diff === 0;
}

// Deliveries are at-least-once: store evt ids and ignore a second arrival.

الأحداث

  • trip.started مفتاح التشغيل يعمل والمركبة تتحرك
  • trip.completed مفتاح التشغيل متوقف منذ دقيقتين
  • geofence.entered / exited عبور حد رسمته
  • alert.raised أي قاعدة تنبيه، مع درجة خطورتها
  • unit.silent لا بيانات بعد حد التقادم
  • job.status سائق ينقل مهمة إلى مرحلتها التالية في التطبيق

كل موقع يحمل

  • position
  • سرعة
  • heading
  • ignition
  • odometer
  • fuel_pct
  • driver_id
  • accel
  • gsm
  • recorded_at
  • received_at

في أي باقة

EssentialProfessionalCommand
REST API وwebhooks قراءة فقط قراءة فقط كامل
تصدير بيانات مجدول

الصفوف نفسها التي في جدول الأسعار، مقروءة منه. والتصدير المجدول في باقة Command وحدها، أما تصدير سجلك كاملا مرة واحدة فمتاح في كل باقة، لأنه بياناتك.

ما نعد به وما لا نعد به

مرة واحدة على الأقل، لا مرة واحدة بالضبط
قد يصل webhook مرتين بعد إعادة المحاولة. لكل حدث معرف id، فاحتفظ بما رأيته منها.
الترتيب غير مضمون
التعبئة اللاحقة بعد منطقة بلا تغطية تسلم مواقع قديمة متأخرة. رتب حسب recorded_at لا حسب وقت وصولها.
لا نبني التكامل عنك
نوثق، ونجيب عن الأسئلة، وننظر في سجلاتك معك. أما الكود عندك فهو لك، وخطته لك.
الكتابة على باقة Command فقط
القراءة متاحة في كل الباقات. أما إدخال المهام والسائقين والأسيجة الجغرافية، والتصدير المجدول، فتحتاج إلى باقة Command.