ناطق
  1. الرئيسية
  2. للمطوّرين
  3. دليل الواجهة

دليل الواجهة البرمجية

مواصفة واجهة REST لنسختك من ناطق. المنفَّذ اليوم نقطة واحدة هي فحص الحالة، والباقي يُبنى عند الطلب.

اقرأ هذا أولاً. ما تحته شارة مُنفَّذ يعمل في الكود اليوم. وما تحته مواصفة شكلٌ نلتزم به ولم يُبنَ بعد، ويُبنى لنسختك عند الطلب. ننشره هنا قبل البناء حتى تخطّط عليه.
المنفَّذ اليوم نقطة واحدة: /health. وكل ما عداها مواصفة، ومنه المصادقة والحدود والويبهوكس.
وكل نقطة في الصفحة تحمل شارة حالتها.

الخطوة الأولى

بداية في خمس دقائق

النداء الوحيد الذي يعمل اليوم بلا مفتاح ولا إعداد هو فحص الحالة. ابدأ به لتتأكّد أن نسختك تعمل وأن الشبكة تصل إليها.

GET /health مُنفَّذ

يردّ 200 بجسم ثابت. لا يحمل بيانات ولا يحتاج مصادقة، فيصلح لأداة مراقبة الخدمة عندك.

# استبدل النطاق بنطاق خادمك
curl https://bot.example.sa/health

# الرد
{ "ok": true }

ثم اطلب مفتاحاً من فريق التأسيس، واحفظه في متغيّر بيئة. لا تضعه في شيفرة الواجهة الأمامية، لأن المفتاح يمنح صلاحية كاملة على سجلّات عملائك.

export NAATEQ_URL=https://bot.example.sa
export NAATEQ_KEY=nq_live_…

curl "$NAATEQ_URL/api/v1/conversations?limit=1" \
  -H "Authorization: Bearer $NAATEQ_KEY"
مكان الواجهة

العنوان الأساسي هو نطاقك أنت

لكل عميل نسخته الخاصة من ناطق، فلا يوجد عنوان واحد لكل العملاء. عنوانك هو النطاق الذي رُكّب عليه نظامك:

https://<نطاقك>/api/v1

ومع التشغيل على خادمك تبقى محادثات عملائك داخل منشأتك، إلا قراءة المستندات والصور فتحتاج مزوّداً سحابياً، كما تشرح صفحة الأمان. فالخادم والمفتاح وسجلّ الوصول كلها عندك.

وفي المقابل، استمرار الخادم في العمل مسؤوليتك. لا نلتزم بنسبة تشغيل لخادم لا نديره، وما نلتزم به مكتوب في صفحة الدعم.

المصادقة

مفتاح واحد في الترويسة

كل طلب يحمل مفتاحك في Authorization. المفتاح يُعرض مرة واحدة عند الإنشاء ولا يُسترجع بعدها. إن ضاع فأنشئ غيره وألغِ القديم.

HEADER Authorization: Bearer <key> مواصفة
أنواع المفاتيح
البادئةالنطاقأين تستعمله
nq_live_قراءة وكتابةخادمك الخلفي حصراً
nq_ro_قراءة فقطلوحات وتقارير
nq_test_قراءة وكتابة على بيانات وهميةللتطوير، ولا يرسل أي رسالة واتساب

مفتاح لكل تكامل. فإلغاء مفتاح لا يوقف البقية، والسجلّ يُظهر ما فعله كل تكامل.

# مفتاح ناقص أو ملغى
HTTP/1.1 401 Unauthorized

{
  "error": {
    "type": "authentication_error",
    "code": "key_revoked",
    "message": "المفتاح مُلغى. اطلب مفتاحاً جديداً.",
    "request_id": "req_01J8…"
  }
}
مورد

المحادثات والرسائل

كل محادثة معزولة برقم العميل، والعزل مبنيّ في النظام ويعمل دائماً، فلا يصل أي نداء إلى سجلّ عميل آخر.

GET /api/v1/conversations مواصفة
معاملات الاستعلام
المعاملالنوعالوصف
stageenuminquiry · quoted · signed · delivered
updated_afterISO 8601ما تغيّر بعد وقت معيّن، وهو الأنسب للمزامنة الدورية
limitintegerالافتراضي 50، الأقصى 200
cursorstringمن next_cursor في الرد السابق

المراحل الأربع أعلاه هي التي يسجّلها النظام في سجل العميل عند كل انتقال.

POST /api/v1/messages مواصفة

يرسل رسالة نصية باسم نشاطك. وتمرّ بفلتر المخرجات نفسه الذي يمرّ به ردّ النظام، فما يُمنع في ردود النظام يُمنع في الواجهة أيضاً.

POST /api/v1/messages
Authorization: Bearer $NAATEQ_KEY
Idempotency-Key: a3f1c9de-…

{
  "to": "966501234567",
  "text": "جهّزنا لك العرض، تحب نرسله؟"
}
مورد

العملاء

سجلّ العميل هو نفسه الذي يكتبه النظام، والحقول أدناه منقولة منه كما هي. فما تقرؤه من الواجهة هو ما يراه النظام لحظة الردّ.

GET /api/v1/customers/{number} مواصفة
حقول سجلّ العميل
الحقلالنوعالوصف
numberstringرقم الجوال بصيغة دولية بلا رموز
namestring | nullnull حتى يذكر العميل اسمه بنفسه
stageenumالمرحلة الحالية من الأربع
projectstring | nullوصف المشروع كما استخلصه النظام
quoteobject | nullamount · project · status · date
contractobject | nullالنطاق داخله وخارجه · تاريخ التسليم · خطة الدفع · تاريخ التوقيع
milestonesarrayمراحل التنفيذ المتّفق عليها
ticketsarraytype · detail · status
payment_statusstring | nullحالة التحصيل الحالية
payment_linksarrayروابط الدفع الصادرة وحالتها
next_payment_duedate | nullتاريخ الدفعة القادمة
deliveredobject | nullتاريخ التسليم ومدّة الضمان بالأشهر
created_atISO 8601لحظة أول رسالة
updated_atISO 8601وقت آخر تغيير، استعمله في updated_after
{
  "number": "966501234567",
  "name": "محمد الحربي",
  "stage": "quoted",
  "project": "مجلس 12 متر — تفصيل",
  "quote": {
    "amount": 4200,
    "project": "مجلس 12 متر — تفصيل",
    "status": "sent",
    "date": "2026-08-04"
  },
  "contract": null,
  "milestones": [],
  "tickets": [],
  "payment_links": [],
  "next_payment_due": null,
  "created_at": "2026-08-04T09:12:44.108Z",
  "updated_at": "2026-08-04T09:31:02.771Z"
}
مورد

المستندات

عروض الأسعار والعقود. يُبنى رقم المستند من بادئة نشاطك وحرف النوع والتاريخ وتسلسل من ثلاث خانات:

HT-Q-260804-058
│    │    │        └── تسلسل اليوم
│    │    └────────── YYMMDD
│    └─────────────── Q عرض سعر · C عقد
└──────────────────── بادئة نشاطك

⚠ البادئة تتغيّر بتغيّر حزمة النشاط، فـلا تكتبها ثابتةً في تكاملك وطابِق النمط كاملاً.

GET /api/v1/documents/{doc_number} مواصفة

يُرجع بيانات المستند وسطوره. وللملفّ نفسه أضف /pdf، فيردّ 302 إلى رابط موقّع صالح لمدة 15 دقيقة.

حقول المستند
الحقلالنوعالوصف
doc_numberstringالمعرّف الفريد، ونمطه مشروح أعلاه
kindenumquote · contract
linesarrayسطور التسعير: الوصف والكمية وسعر الوحدة
totalintegerالإجمالي بالريال
payment_planstringملخّص خطة الدفع المحسوبة من الإجمالي
approved_by_ownerbooleanالعقد لا يُرسَل للعميل قبل أن تصير true
مورد

المدفوعات

المبالغ بالهللات دائماً، وبأعداد صحيحة. فـ4,200 ريال تُكتب 420000. الخلط بين الريال والهللة من أكثر أسباب الأخطاء المالية في تكاملات الدفع، لذلك لا يتعامل النظام داخلياً مع كسور عشرية، والتحويل يحدث عند الأطراف فقط.

ويحسب النظام خطة الدفع من الإجمالي. الدفعة الأخيرة تُحسب بطرح ما سبقها من الإجمالي، فيساوي مجموع الدفعات الإجمالي بالضبط مهما كان التقريب، ولا يظهر فرق ريال عند التحصيل:

جدول الدفعات لإجمالي 12,000 ريال فأكثر
الدفعةالنسبةتُستحقّ عند
الأولى50%التوقيع وبدء العمل
الثانية30%التسليم الأولي
الأخيرة20%التسليم النهائي، وتُحسب بالطرح
GET /api/v1/gateways مواصفة

يُرجع حالة كل بوابة في نسختك: هل هي مهيّأة، ووضعها، وحدّها الأدنى، وأسماء حقول اعتمادها.

[
  { "id": "moyasar", "label_ar": "مُيسّر",
    "configured": true, "mode": "live",
    "min_amount_minor": 100,
    "fields": ["secret_key"] },
  { "id": "paylink", "label_ar": "Paylink",
    "configured": false, "mode": null,
    "min_amount_minor": 500,
    "fields": ["api_id", "secret_key"] },
  { "id": "tap", "label_ar": "تاب",
    "configured": false, "mode": null,
    "min_amount_minor": 100,
    "fields": ["secret_key"] }
]

نتحقق من حدّ Paylink الأدنى (5.00 ريال) قبل إرسال الطلب إليهم، فيصلك الخطأ فوراً من خادمك.

POST /api/v1/payment-links مواصفة

المبلغ يُقرأ دائماً من جدول دفعات العقد المعتمَد. وهذا قيد في الكود، فلا توجد أداة بيد النموذج تقبل مبلغاً. والواجهة تخضع للقيد نفسه، لذلك تُرسل instalment وليس amount.

حقول إنشاء رابط الدفع
الحقلالنوعالوصف
doc_numberstring مطلوبعقد معتمَد من المالك
instalmentinteger مطلوبرقم الدفعة في جدول العقد، ومنها يُقرأ المبلغ
providerenummoyasar · paylink · tap، والافتراضي أول بوابة مهيّأة
idempotency_keyuuidيمنع رابطين لدفعة واحدة
{
  "provider": "moyasar",
  "url": "https://…",
  "ref": "inv_01J8…",
  "status": "pending",
  "amount_minor": 210000,
  "currency": "SAR"
}
أحداث صادرة

الويبهوكس

تسجّل رابطاً واحداً، فتصلك الأحداث المشتركة عليه بصيغة JSON مع توقيع X-Naateq-Signature.

الأحداث كلّها مواصفة، ولا يُرسَل أي منها بعد.

message.receivedوصلت رسالة من عميل. تحمل النص ورقم المرسل والنية المصنَّفة.
reply.sentردّ النظام. تحمل الرد والمصدر (آلي · أداة · بشري) وزمن المعالجة.
stage.changedانتقل العميل بين المراحل الأربع. تحمل المرحلة السابقة والجديدة.
quote.issuedصدر عرض سعر. تحمل رقم المستند والإجمالي وخطة الدفع.
contract.approvedوافق المالك على إرسال العقد. تحمل رقم العقد وقيمته.
payment.recordedأُثبتت دفعة. تحمل رقم الدفعة والمبلغ بالهللات والبوابة.
handoff.requestedطُلب تصعيد لموظف بشري. تحمل سبب التصعيد.
guard.blockedحجب فلتر المدخلات رسالة. تحمل نوع المحاولة.
POST /your-endpoint
X-Naateq-Signature: t=1785838800,v1=5f2b…
X-Naateq-Delivery: whd_01J8…

{
  "event": "quote.issued",
  "created_at": "2026-08-04T09:31:02.771Z",
  "data": {
    "doc_number": "NQ-Q-260804-058",
    "customer": { "number": "966501234567", "name": "محمد الحربي" },
    "total": 420000,
    "currency": "SAR",
    "payment_plan": "50% عند التوقيع • 30% عند التسليم الأولي • 20% عند التسليم النهائي"
  }
}
سلوك التسليم
البندالقيمة
النجاحأي رمز 2xx خلال المهلة
مهلة نقطتك5 ثوانٍ
إعادة المحاولة5 محاولات بتباعد أُسّي خلال 24 ساعة
الترتيبغير مضمون، فرتّب حسب created_at
التكرارممكن، فاستعمل X-Naateq-Delivery لتتجاهل الحدث المكرّر

كل نظام يعيد المحاولة قد يرسل الحدث مرتين أو بترتيب مختلف، فابنِ معالجك على هذا الأساس.

الأمان

لا تثق بأي طلب غير موقّع

احسب HMAC-SHA256 على t + "." + الجسم الخام بمفتاح الويبهوك، وقارنه بـv1 بمقارنة ثابتة الزمن. وارفض أي طلب أقدم من خمس دقائق حتى لا يُعاد تشغيل طلب قديم عليك.

import crypto from "node:crypto";

export function verify(raw, header, secret) {
  const [t, v1] = header.split(",").map(p => p.split("=")[1]);
  // نافذة زمنية: تمنع إعادة تشغيل طلب قديم
  if (Math.abs(Date.now() / 1000 - +t) > 300) return false;

  const expect = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${raw}`)
    .digest("hex");

  // المقارنة ثابتة الزمن: == يسرّب الفرق عبر زمن التنفيذ
  const a = Buffer.from(expect), b = Buffer.from(v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

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

واحسب التوقيع على الجسم الخام قبل تحليل JSON، لأن إعادة ترتيب المفاتيح تُفسد التوقيع.

الموثوقية

منع التكرار حتى لا تدفع مرّتين

كل POST يقبل Idempotency-Key. إن انقطع الاتصال ولم يصلك الرد، فأعد الطلب نفسه بالمفتاح نفسه، وستحصل على النتيجة نفسها دون رابط دفع ثانٍ أو رسالة مكرّرة.

سلوك مفتاح منع التكرار
الحالةالسلوك
نفس المفتاح ونفس الجسميُعاد الرد المحفوظ مع Idempotency-Replayed: true
نفس المفتاح وجسم مختلف409 مع idempotency_key_reused
مدّة الحفظ24 ساعة
القوائم

التنقل بين الصفحات بمؤشّر

لا نستعمل page=2، لأن وصول سجلّ جديد بين طلبين يُزيح الترقيم فتفوتك عناصر أو تتكرّر. أما المؤشّر فيحفظ موضعك.

{
  "data": [ … ],
  "has_more": true,
  "next_cursor": "cur_01J8…"
}

وللمزامنة الدورية استعمل updated_after بآخر updated_at استلمته، فتنقل ما تغيّر فقط بدل السجلّ كلّه.

الأخطاء

شكل واحد لكل خطأ

كل خطأ يحمل type للتصنيف وcode للتعامل البرمجي وmessage للعرض وrequest_id للدعم. اعتمد على code في برمجتك وليس message، لأن نص الرسالة قد نحسّنه أما الرمز فثابت.

رموز الأخطاء
الحالةالرمزالسبب وما تفعله
400invalid_requestحقل ناقص أو نوع خاطئ. التفاصيل في errors[]
401key_missing · key_revokedراجع الترويسة أو أنشئ مفتاحاً
403scope_insufficientمفتاح قراءة فقط في نداء كتابة
404not_foundلا سجلّ بهذا المعرّف في نسختك
409idempotency_key_reusedنفس المفتاح بجسم مختلف
409contract_not_approvedطلبتَ رابط دفع لعقد لم يعتمده المالك بعد
422gateway_not_configuredلا توجد بوابة مهيّأة. راجع GET /api/v1/gateways
422amount_below_minimumالمبلغ دون حدّ البوابة الأدنى
429rate_limitedانتظر Retry-After ثم أعد
503gateway_unavailableالبوابة لم تستجب. أعد لاحقاً بنفس مفتاح منع التكرار

⚠ لا تظهر اعتمادات البوابة في أي رسالة خطأ، كاملةً أو مقتطعة. هذه قاعدة مبنية في طبقة الدفع، لأن رسائل الخطأ من أكثر الأماكن التي تتسرّب منها المفاتيح.

الحدود

الحدود وسياسة البيانات

حدود الاستخدام وسياسة البيانات
البندالقيمة
حدّ الطلبات120 / دقيقة لكل مفتاح
ترويسات الحدّX-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset
عند التجاوز429 مع Retry-After بالثواني
حجم الجسم256 كيلوبايت
أين تُخزَّن البياناتعلى خادمك في التشغيل المحلي، أو على خوادمنا في التشغيل المُدار، والذكاء الاصطناعي عند مزوّد سحابي نسمّيه في العقد
مدّة الاحتفاظتضبطها أنت، والنظام لا يحذف شيئاً تلقائياً

الحدود أعلاه مواصفة، ولا يُطبَّق منها شيء بعد. ولأن لكل عميل نسخته، فحدّ الطلبات يحمي موارد نسختك أنت. ويمكن رفعه من إعدادات نسختك دون أن تشتريه منّا.

الاستقرار

الإصدار والإيقاف المجدول

الإصدار في المسار: /api/v1/. وداخل الإصدار الواحد نضيف ولا نحذف.

ما يُعدّ تغييراً كاسراً وما لا يُعدّ
التغييركاسر؟
حقل جديد في الردلا، تجاهل الحقول التي لا تعرفها
قيمة جديدة في enumلا، ولا تجعل القيمة المجهولة تعطّل تكاملك
نقطة نهاية جديدةلا
حذف حقل أو تغيير نوعهنعم، في إصدار جديد
تشديد تحقّق قائمنعم، في إصدار جديد

وإن قرّرنا إيقاف نقطة: نُشعرك قبل 180 يوماً، ونضيف ترويسة Sunset على كل ردّ منها، وتبقى تعمل طوال المدّة.

التاريخ

سجلّ التغييرات

نسجّل هنا كل تغيير في هذا الدليل بتاريخه، ومنه تصحيح ما كُتب خطأً.

2026-08-04 أول نشر لهذا الدليل. وفي الوقت نفسه حذفنا من صفحة المطوّرين أربعة أمور لم تكن مبنيّة: عنوان على نطاق غير محجوز، وسبعة أحداث لا تُرسَل، وحدّ طلبات غير مطبّق، وتصدير وشهادة حذف. وصار لكل نقطة شارة حالة صريحة.
2026-09-28 الأحداث السبعة عادت إلى صفحة المطوّرين بشارة «مواصفة»، ومثالها صار بالهللات كما يقول هذا الدليل. وسُحبت Webhooks من عدّاد «جاهزة اليوم» في صفحة التكاملات.
2026-10-01 أوضحنا حالة الدليل في رأس الصفحة: فحص الحالة وحده منفَّذ، والباقي مواصفة تُبنى عند الطلب. ووسمنا جدول الحدود بشارة «مواصفة»، وصحّحنا مكان معالجة البيانات في التشغيل المُدار.

تبني تكاملاً وتحتاج نقطة غير موجودة؟

نرتّب الأولويات حسب ما يبنيه العملاء. أخبرنا بحالتك وبأي نقطة تحتاج أولاً.