عمليات البحث
اقرأ سجلات العملاء الحيّة عبر طلبات موقّعة إلى نظام عملك.
اقرأ سجلات العملاء الحيّة عبر طلبات موقّعة إلى نظام عملك.
متى تستخدم عملية البحث#
استخدم عملية البحث للاطلاع على حالة الطلب الحالية أو حجز قائم أو سجل حساب العميل. أبقِ السياسات الثابتة ومعلومات المنتجات في المعرفة. تقرأ عملية البحث من نظامك ولا يجوز لها أن تخصم مبلغًا أو تُلغي أو تحجز أو تُغيّر أي سجل بأي شكل آخر. أما بطاقات الاختيار والتأكيد العامة فترسل رسالة الزائر فقط. للكتابة المؤكَّدة استخدم إجراءات التغيير، ولحجوزات التقويم استخدم الحجز عبر Cal.com. وحدها بطاقات التأكيد المرتبطة بتشغيل فعلي تخوّل هذه العمليات.
إعداد الإجراء#
افتح الوكيل ← الإجراءات. أنشئ المفتاح السري في العملاء المسجّلون للدخول، وخزّنه على خادمك ونفّذ تحديد هوية العميل. في عمليات البحث أضف اسمًا مثل order_status، واشرح متى ينبغي للوكيل استخدامها، وأدخل نقطة النهاية العامة عبر HTTPS. صرّح فقط بالمدخلات التي تحتاج إليها، مثل سلسلة نصية order_id. يرى الوكيل الأداة باسم lookup_order_status.
أبقِ خيار يتطلب عميلًا مسجّلًا للدخول مفعّلًا للسجلات الخاصة (وهو الإعداد الافتراضي). إذا عطّلته فقد تحتوي الطلبات على customer: null؛ أعِد فقط المعلومات المناسبة لزائر مجهول. المعرّف المذكور في المحادثة أو المستخرج من صورة هو مُدخل وليس هوية موثَّقة.
تُحفظ الحقول الحالية تلقائيًا؛ انتظر ظهور مؤشر الحفظ قبل الاختبار. يمكنك تفعيل عملية البحث أو تعطيلها أو تعديلها أو حذفها. تتطلب اللوحة إعداد مفتاح سري. تُهيّأ هذه الإجراءات في لوحة التحكم؛ ولا توفّر الموجة 1 إدارة عمليات البحث عبر API العامة.
الطلب الذي تستقبله#
يرسل Make Agent Fast طلب JSON من نوع POST إلى عنوان URL الذي هيّأته. تستخدم الأمثلة أدناه قيمًا نائبة للتوقيع:
POST /agent/order-status HTTP/1.1
Content-Type: application/json
x-maf-timestamp: <unix-seconds>
x-maf-signature: v1=<64-lowercase-hex-characters>{
"version": 1,
"action": "order_status",
"requestId": "c5c1d94b-e88f-4bf6-8023-2dcd1e7ac67b",
"siteId": "YOUR_SITE_ID",
"input": { "order_id": "A128" },
"customer": { "id": "customer_123", "email": "customer@example.com" }
}action هو الاسم المهيَّأ دون البادئة lookup_. يحدد requestId هذه المحاولة بعينها، ويحدد siteId موقعك. يحتوي input على المعاملات النصية المصرَّح بها فقط. أما customer فهو إما null أو الكائن { id, email?, name? } الذي تحقق منه الخادم؛ ولا يمكن لمدخلات الزائر أو النموذج أن تحل محله. يستخدم اختبار في لوحة التحكم عن قصد عميلًا تجريبيًا يُدخله مالك مخوَّل.
التحقق من التوقيع#
التوقيع هو HMAC-SHA256 بصيغة سداسية عشرية بأحرف صغيرة باستخدام المفتاح السري للوكيل، ويسبقه v1=. وقّع بدقة السلسلة "maf-webhook-v1:" + timestamp + "." + rawBody. حافظ على البايتات كما وصلت؛ فتحليل JSON ثم إعادة تسلسله قبل فحص التوقيع قد يغيّرها. تقبل هذه الدالة المساعدة كائن Request قياسيًا من جهة الخادم، وتطبّق نافذة صلاحية مدتها خمس دقائق، وتثبّت الموقع:
import { createHmac, timingSafeEqual } from "node:crypto";
export async function verifyLookupRequest(request, expectedSiteId) {
const secret = process.env.MAF_SECRET_KEY;
if (!secret) throw new Error("MAF_SECRET_KEY is required");
const timestamp = request.headers.get("x-maf-timestamp");
const signature = request.headers.get("x-maf-signature");
const now = Math.floor(Date.now() / 1000);
if (!timestamp || !/^\d+$/.test(timestamp) ||
!Number.isSafeInteger(Number(timestamp)) ||
Math.abs(now - Number(timestamp)) > 300) {
throw new Error("Invalid timestamp");
}
if (!signature || !/^v1=[a-f0-9]{64}$/.test(signature)) {
throw new Error("Invalid signature");
}
const rawBody = Buffer.from(await request.arrayBuffer());
const expected = createHmac("sha256", secret)
.update(`maf-webhook-v1:${timestamp}.`)
.update(rawBody)
.digest();
const actual = Buffer.from(signature.slice(3), "hex");
if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
throw new Error("Invalid signature");
}
const body = JSON.parse(rawBody.toString("utf8"));
if (body?.version !== 1 || body.siteId !== expectedSiteId) {
throw new Error("Invalid lookup scope");
}
return body;
}استدعِها قبل أن يستهلك محلل الجسم (body parser) الطلب. ينبغي أن يرفض معالجك أخطاء التحقق، وأن يتحقق من الإجراء المتوقع وشكل المدخلات، وأن يفحص ملكية السجل مقابل body.customer.id قبل إعادة أي بيانات خاصة. على سبيل المثال، اختر الطلب A128 من بين طلبات ذلك العميل، لا بمعرّف الطلب وحده. لا تستخدم أبدًا input.customer_id أو بريدًا إلكترونيًا مقدَّمًا في المحادثة أو صورة كأساس للتخويل. اشترط عميلًا غير null لنقاط النهاية الخاصة. حدّد حجم الجسم الوارد على خادمك، وتتبّع requestId ضمن نافذة الصلاحية إذا كنت تريد رفض إعادة الإرسال؛ فالتحقق من الطابع الزمني وحده لا يرفض إعادة إرسال تقع ضمن تلك النافذة.
استخدم HTTPS وأبقِ نقطة النهاية هذه للقراءة فقط رغم أن وسيلة النقل هي POST. بعد تدوير المفتاح السري للوكيل، حدّث كلًا من خادم توقيع الهوية ومُتحقِّق عمليات البحث لديك.
إرجاع الاستجابة#
أجب بحالة نجاح 2xx وبـ JSON مطابق لهذا العقد. المثال الأول سجل تم العثور عليه، والثاني نتيجة عادية بلا تطابق:
{
"found": true,
"card": {
"title": "Order #A128",
"status": { "label": "Shipped", "tone": "success" },
"fields": [{ "label": "Carrier", "value": "DHL" }],
"reference": "A128",
"asOf": "2026-10-12T09:00:00Z",
"link": { "label": "Track", "url": "https://shop.example.com/orders/A128" }
},
"facts": { "order_id": "A128", "status": "shipped", "carrier": "DHL" }
}{ "found": false }الحقل found مطلوب. عندما تكون القيمة found: true أدرج card أو facts أو كليهما. تتطلب البطاقة title وfields (يُسمح بمصفوفة فارغة). الحقول الاختيارية هي status وreference وasOf وlink. لا تضف kind إلى بطاقة الاستجابة. درجات الحالة هي neutral وsuccess وwarning وdanger. استخدم طابعًا زمنيًا بصيغة ISO للحقل asOf، ورابط HTTPS بلا بيانات اعتماد مضمَّنة. تُحذف الروابط الاختيارية غير الصالحة.
facts كائن مسطّح قيمه نصوص أو أعداد منتهية أو قيم منطقية أو null؛ وتُرفض الكائنات والمصفوفات المتداخلة. تُرفض الحقول غير المعروفة في مخطط الاستجابة/البطاقة. أعِد فقط المعلومات اللازمة للإجابة: يتلقى النموذج بيانات مُهرَّبة ومحدودة موسومة بأنها غير موثوقة، لا تعليمات. محتوى البطاقة نص عادي وليس HTML.
الحدود وسلوك الفشل#
| البند | الحد |
|---|---|
| عمليات البحث لكل وكيل | 5؛ بأسماء ومعرّفات فريدة |
| الاسم | ^[a-z][a-z0-9_]{2,39}$؛ أسماء الإجراءات المحجوزة غير متاحة |
| الوصف | 300 حرف |
| المدخلات المصرَّح بها | 3؛ بأسماء فريدة تطابق ^[a-z][a-z0-9_]{1,31}$ |
| أوصاف المدخلات / قيمها | 300 / 200 حرف؛ تُقتطع المسافات الزائدة من القيم |
| نقطة النهاية | HTTPS عامة، حتى 500 حرف، بلا بيانات اعتماد؛ تُحجب العناوين الخاصة/الداخلية؛ لا تُتبع إعادات التوجيه |
| المهلة / جسم الاستجابة | 8 ثوانٍ / 32 KiB (32,768 بايت) |
| بيانات النموذج | 2,000 حرف، منفصلة عن بطاقة الزائر |
| عنوان البطاقة / التسميات / قيم الحقول | 80 / 40 / 200 حرف |
| حقول البطاقة | 8؛ وبحد أقصى 3 بطاقات في كل دور للوكيل |
يؤدي غياب هوية العميل إلى عرض تنبيه بتسجيل الدخول دون استدعاء نقطة النهاية. وتدفع المدخلات المطلوبة المفقودة الوكيل إلى سؤال الزائر عنها. يعرض found: false بطاقة محايدة تفيد بعدم وجود تطابق. أما انتهاء المهلة أو حالات غير 2xx أو JSON/مخطط غير صالح أو الاستجابات كبيرة الحجم فتعرض حالة فشل وتطلب من النموذج ألا يخمّن. أعِد found: false مع حالة 2xx للسجل المفقود عادةً، بدلًا من HTTP 404. اجعل الحقائق المهمة موجزة: تُقتطع نصوص البطاقة وبيانات النموذج الزائدة.
الاختبار من لوحة التحكم#
- احفظ عملية البحث وافتح اختبار. أدخل معرّف عميل تجريبيًا، وبريدًا إلكترونيًا اختياريًا، وجميع المدخلات المطلوبة. استخدم حسابًا تتحكم فيه.
- شغّل اختبار. يرسل هذا طلبًا موقّعًا حقيقيًا حتى لو كانت عملية البحث معطّلة للزوار؛ أما محادثة التجربة العادية فلا تستدعي نقطة النهاية أبدًا. تحدّ لوحة التحكم من محاولات الاختبار لكل مالك.
- افحص البطاقة/الملخص الذي تم التحقق منه أو الخطأ، ومعرّف الطلب، والمدة، وحالة HTTP عند توفرها. راجع سجلات نقطة النهاية لديك دون تسجيل الأسرار أو رموز العملاء.
- اختبر حالات: العثور على السجل، وعدم العثور عليه، وحساب خاطئ، ومدخل مفقود، وانتهاء المهلة. ثم اختبر التضمين الفعلي مع عميلين موثَّقين؛ فالعميل التجريبي في لوحة التحكم لا يثبت عزل هوية المتصفح ولا اختيار النموذج الفعلي للأداة.
تعرض الأداة المضمَّنة بطاقات الحالة؛ وتُرسل نقرات الاختيار/التأكيد العامة تسمياتها كرسائل. أما تأكيدات التغيير والحجز المرتبطة بتشغيل فعلي فتستخدم مسار التنفيذ المحمي بدلًا من ذلك. تكيّف الموصّلات البطاقات إلى نصوص/بطاقات أصلية وأزرار أو ردود سريعة أو قوائم، مع بديل نصي رهنًا بحدود كل قناة. اختبر كل قناة مقصودة قبل الاعتماد على التسليم.