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

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

واجهة REST فوق نسختك من ناطق. حمولاتها هي سجلّات نظامك نفسها — لا نماذج مُصاغة للعرض.

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

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

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

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

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 في الرد السابق

المراحل الأربع أعلاه هي مراحل النظام الفعلية لا تصنيفاً تسويقياً — يكتبها crm.js عند كل تحوّل.

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

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

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

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

العملاء

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

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"
}
مورد

المستندات

عروض الأسعار والعقود. ورقم المستند ليس عشوائياً — يبنيه docNumber() من بادئة نشاطك وحرف النوع والتاريخ وتسلسل من ثلاث خانات:

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": "HT-Q-260804-058",
    "customer": { "number": "966501234567", "name": "محمد الحربي" },
    "total": 4200,
    "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
نفس المفتاح وجسم مختلف409idempotency_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 أول نشر لهذا الدليل. وفي الوقت نفسه سُحبت من صفحة المطوّرين أربع دعاوى لم يكن يقابلها كود: مضيفٌ على نطاق غير محجوز، وسبعة أحداث بلا باعث، وحدّ طلبات بلا محدِّد، وتصديرٌ وشهادةُ حذفٍ لم يُبنيا. وصار لكل نقطة شارة حالة صريحة.

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

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