بواسطة Amy Baker
نُشر في 25 يوليو 2026
كيفية إرسال رسالة قالب WhatsApp باستخدام API
هل تنشئ نظامًا للإشعارات على WhatsApp؟ يشرح هذا الدليل الطرق الثلاث لإرسال رسائل القوالب عبر API الخاص بـ Chakra، ونمطي استبدال المتغيرات، ومعالجة الخطأين 131049 و131026، وتتبع التسليم عبر webhooks، والإرسال الجماعي الخاضع لحدود معدل الطلبات.
إذا كنت تنشئ أنظمة إشعارات على WhatsApp — لتحديثات الطلبات، وتذكيرات المواعيد، ورموز OTP، وتدفقات إعادة التفاعل — فستحتاج في نهاية المطاف إلى إرسال رسالة قالب برمجيًا. يتيح لك Chakra ثلاث طرق مختلفة لتنفيذ ذلك، تناسب كل منها نمط تكامل مختلفًا. يغطي هذا الدليل الطرق الثلاث كلها: بنية الطلبات، ومتى تختار كل طريقة، واستبدال المتغيرات، ومعالجة الخطأين الأكثر شيوعًا في بيئة الإنتاج — 131049 و 131026.
يفترض هذا أنك تجاوزت بالفعل مرحلة "ما قالب WhatsApp؟" — لديك WABA معتمد، وقوالب معتمدة، ومفتاح API. إذا كنت بحاجة إلى إعداد ذلك، فراجع قسم مفاتيح API في Chakra لإنشاء رمز bearer الخاص بك.
المصادقة باختصار
يتطلب كل طلب رمز bearer تم إنشاؤه باستخدام مفتاح API ذي نطاق الدور/الصلاحيات المناسب:
curl --request GET \ --url https://api.chakrahq.com/v1/ext/config \ --header 'Authorization: Bearer <your_access_token>'احصر نطاق المفاتيح في أقل قدر من الصلاحيات اللازمة، خصوصًا أي شيء يتعامل مع شيفرة جهة العميل. ومن المفيد معرفته مسبقًا أيضًا: يفرض Chakra حدًا لمعدل الطلبات يبلغ 200 API calls/minute — لذا احتسب ذلك ضمن أي حلقة إرسال جماعي، بدلًا من اكتشافه عبر أخطاء 429 في بيئة الإنتاج.
الطرق الثلاث لإرسال رسالة قالب
| الطريقة | نمط نقطة النهاية | الأنسب لـ |
|---|---|---|
| حسب العملية | .../process/{procedureShortId}/{primaryKeyOrId}/send-template-message | إرسال مرتبط بسجل في CRM — عميل محتمل أو تذكرة أو حالة موجودة بالفعل في Chakra |
| حسب رقم الهاتف | .../phoneNumber/{phoneNumber}/send-template-message | عمليات إرسال مستقلة وبسيطة عندما يكون رقم المستلم متوفرًا لديك ولا تحتاج إلى ربط الرسالة بسجل |
| تنسيق API الخاص بـ Meta | .../api/{whatsappApiVersion}/{whatsappPhoneNumberId}/messages | تحكم كامل وحمولة Meta Cloud API مطابقة تمامًا — مفيد إذا كنت تنقل شيفرة تكامل موجودة مع Meta أو تحتاج إلى أنواع رسائل لا تغطيها نقاط النهاية المبسطة في Chakra |
تستخدم الطرق الثلاث في النهاية بنية WhatsApp الأساسية نفسها — والاختلاف في شكل الحمولة والسياق الذي يرفقه Chakra بعملية الإرسال.
الطريقة 1: الإرسال حسب العملية
استخدم هذه الطريقة عندما تكون الرسالة مرتبطة بسجل موجود بالفعل في منصة Chakra — مثل عميل محتمل أو تذكرة أو زيارة ميدانية أو أي نوع عملية مخصص. وهي الخيار المناسب عندما تريد تسجيل الرسالة الصادرة تلقائيًا ضمن ذلك السجل، من دون خطوة منفصلة لربطهما.
نقطة النهاية:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/process/{procedureShortId}/{primaryKeyOrId}/send-template-messagepluginId— معرّف UUID لاتصال WhatsApp لديك، ويمكن نسخه من صفحة تفاصيل الإضافة في شاشة الإعدادprocedureShortId— نوع السجل (lead,ticket,case، أو shortId لعملية مخصصة)primaryKeyOrId— معرّف السجل المحدد أو مفتاحه الأساسي
مثال باستخدام curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/process/lead/8842/send-template-message' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "whatsappPhoneNumberId": "775966265503012", "templateName": "order_confirmation", "mapping": [ { "schemaPropertyName": "1", "schemaPropertyValue": "John" }, { "schemaPropertyName": "2", "schemaPropertyValue": "ORD-4521" } ] }'JavaScript (fetch):
async function sendTemplateForLead(leadId, templateName, variables) { const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/process/lead/${leadId}/send-template-message`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ whatsappPhoneNumberId: PHONE_NUMBER_ID, templateName, mapping: variables.map((value, i) => ({ schemaPropertyName: String(i + 1), schemaPropertyValue: value, })), }), } ); if (!response.ok) { const error = await response.json(); throw new Error(`Template send failed: ${JSON.stringify(error)}`); } return response.json();}الطريقة 2: الإرسال حسب رقم الهاتف
حمولة مطابقة وظيفيًا للطريقة 1، لكنك ترسل مباشرةً إلى رقم هاتف بدلًا من سجل عملية مرتبط. استخدم هذه الطريقة لأي حالة لا يهم فيها ارتباط السجل — مثل إرسال تُطلقه webhook من نظام خارجي (Shopify أو WooCommerce أو الواجهة الخلفية الخاصة بك) ولا يحتاج إلى عميل محتمل مقابل في Chakra.
نقطة النهاية:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/phoneNumber/{phoneNumber}/send-template-messageمثال باستخدام curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/phoneNumber/919901258433/send-template-message' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "whatsappPhoneNumberId": "775966265503012", "templateName": "shipping_update", "mapping": [ { "schemaPropertyName": "1", "schemaPropertyValue": "Priya" } ], "imageUrl": "https://your-cdn.com/shipping-banner.png" }'JavaScript (fetch):
async function sendTemplateByPhone(phoneNumber, templateName, mapping, imageUrl) { const body = { whatsappPhoneNumberId: PHONE_NUMBER_ID, templateName, mapping }; if (imageUrl) body.imageUrl = imageUrl; const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/phoneNumber/${phoneNumber}/send-template-message`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(body), } ); return handleWhatsAppResponse(response); // shared error handler, see below}لاحظ حقل imageUrl — إذا كان القالب يتضمن مكوّن صورة في الترويسة، فأدخل هنا عنوان URL متاحًا للعامة بدلًا من تحميل الوسائط بشكل منفصل، إلا إذا كنت تستخدم نقطة النهاية بتنسيق Meta، التي تتطلب معرّف وسائط بدلًا من ذلك.
الطريقة 3: تنسيق Meta API (تمرير مباشر للبيانات الخام)
نقطة النهاية هذه ليست سوى غلاف بسيط — إذ يمرّر Chakra حمولتك مباشرةً إلى Cloud API الخاصة بـ Meta، وفق مخطط Meta نفسه. استخدمها عندما يكون لديك بالفعل منطق حمولة بتنسيق Meta (منقول من تكامل سابق مباشر مع Cloud API، أو مبني وفق وثائق Meta)، أو عندما تحتاج إلى أنواع رسائل أو مكوّنات لا تغطيها نقاط النهاية المبسّطة في Chakra، أو عندما تريد تطابقًا تامًا بايتًا ببايت مع عقود الطلب والاستجابة لدى Meta لأغراض التسجيل الخاصة بك.
نقطة النهاية:
POST https://api.chakrahq.com/v1/ext/plugin/whatsapp/{pluginId}/api/{whatsappApiVersion}/{whatsappPhoneNumberId}/messagesمثال باستخدام curl:
curl --location --request POST \ 'https://api.chakrahq.com/v1/ext/plugin/whatsapp/d83e1d23-50b8-4d87-8f92-842a0ac516f6/api/v19.0/775966265503012/messages' \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data-raw '{ "messaging_product": "whatsapp", "recipient_type": "individual", "to": "919901258433", "type": "template", "template": { "name": "order_confirmation", "language": { "policy": "deterministic", "code": "en_US" }, "components": [ { "type": "body", "parameters": ["John", "ORD-4521"] } ] } }'JavaScript (fetch):
async function sendTemplateMetaFormat(to, templateName, langCode, bodyParams) { const response = await fetch( `https://api.chakrahq.com/v1/ext/plugin/whatsapp/${PLUGIN_ID}/api/v19.0/${PHONE_NUMBER_ID}/messages`, { method: "POST", headers: { "Authorization": `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ messaging_product: "whatsapp", recipient_type: "individual", to, type: "template", template: { name: templateName, language: { policy: "deterministic", code: langCode }, components: [{ type: "body", parameters: bodyParams }], }, }), } ); return handleWhatsAppResponse(response);}تقبل نقطة النهاية نفسها أيضًا حمولات Meta الأصلية لرسائل الجلسة (غير المقيّدة بقالب) — النصوص والصور والقوائم التفاعلية — لأنها تمرير مباشر إلى /messages الأساسية، وليست نقطة نهاية مخصّصة للقوالب فقط.
استبدال المتغيرات: نمطان مختلفان
قد يسبب هذا الأمر بعض الالتباس عند التبديل بين الطرق، لذا من المفيد توضيحه صراحةً:
الطريقتان 1 و2 (طبقة التجريد في Chakra) تستخدمان مصفوفة mapping تتضمن أزواجًا على الشكل { schemaPropertyName, schemaPropertyValue } . يشير schemaPropertyName إلى موضع المتغير في القالب كسلسلة نصية ("1", "2"، ...)، أما schemaPropertyValue فهو المحتوى الذي سيحل محل المتغير. هذا هو المخطط المبسّط الخاص بـ Chakra، والمنفصل عن بنية مكوّنات Meta الخام.
الطريقة 3 (تنسيق Meta) تستخدم مصفوفة Meta الأصلية components ، حيث يحتوي كل مكوّن (header, body, button) على مصفوفة parameters خاصة به، وتُملأ المتغيرات بحسب ترتيبها. إذا كان القالب يحتوي على عدة متغيرات في النص الأساسي، فإن parameters: ["John", "ORD-4521"] يملأ {{1}} و {{2}} على التوالي، وفق ترتيب المصفوفة — فلا يوجد حقل فهرس صريح، لذا من المهم ضبط الترتيب بشكل صحيح.
إذا كنت تبني نظامًا قد يحتاج لاحقًا إلى التبديل بين نقاط النهاية (مثلًا، تبدأ ببساطة ثم تحتاج لاحقًا إلى ميزات Meta الخام)، فمن الأفضل كتابة دالة محوّل داخلية تحوّل قائمة المتغيرات الخاصة بك إلى التنسيق الذي تتطلبه نقطة النهاية المستخدمة، بدلًا من تكرار منطق ربط المتغيرات في أنحاء قاعدة التعليمات البرمجية.
التعامل مع الأخطاء التي ستواجهها فعليًا
الخطأ 131049 — تفاعل المنظومة / لم يتم التسليم. هذا ليس خطأً في تنسيق الطلب؛ بل إن نظام WhatsApp يرفض التسليم لأن من المرجح أن المستلم لا يرغب فيه (بسبب انخفاض التفاعل مؤخرًا، أو ارتفاع معدلات الحظر، أو بلوغ الحد التسويقي لكل مستخدم عبر الأنشطة التجارية عمومًا). لا تُعِد محاولة الإرسال نفسه فورًا — فحلقة إعادة المحاولة الفورية ستكرر الإخفاق وقد تزيد وضع جهة الاتصال سوءًا. سجّل الخطأ، واستثنِ جهة الاتصال من دورة الإرسال التالية، ثم أعد المحاولة بعد فترة تأخير (24 ساعة حد أدنى معقول)، باستخدام قالب Utility إذا سمح السياق بذلك.
الخطأ 131026 — المستلم غير موجود على WhatsApp أو يتعذر الوصول إليه. الرقم غير صالح على WhatsApp، أو لم يوافق المستلم على الشروط الحالية، أو لا يمكنه استقبال نوع الرسالة هذا. هذه مشكلة في جودة البيانات وليست مشكلة عابرة — ولن يؤدي تكرار المحاولة إلى حلها. أشر إلى جهة الاتصال لتنظيف القائمة بدلًا من إعادة إدراجها في قائمة الانتظار.
قد يبدو معالج أخطاء مشترك للطرق الثلاث كما يلي:
async function handleWhatsAppResponse(response) { if (response.ok) return response.json(); const errorBody = await response.json(); const errorCode = errorBody?.error?.code || errorBody?._data?.error?.code; switch (errorCode) { case 131049: // Don't retry now. Queue for a delayed retry, exclude from current campaign. await flagForDelayedRetry(errorBody, { minDelayHours: 24 }); break; case 131026: // Bad number — don't retry, flag for cleanup. await flagInvalidNumber(errorBody); break; default: // Log and surface unexpected errors for manual review. console.error("Unhandled WhatsApp send error:", errorBody); } throw new Error(`WhatsApp send failed (${errorCode}): ${JSON.stringify(errorBody)}`);}إن بناء هذا النوع من التصنيف في مسار الإرسال مبكرًا يجنبك حلقة إعادة محاولة تواصل الضغط بصمت على الأرقام نفسها التي تفشل، وهذا من أسرع السبل للإضرار بتقييم الجودة.
تتبّع حالة التسليم بعد الإرسال
لا تؤكد استجابة 200 من أي من نقاط النهاية الثلاث سوى قبول الرسالة للإرسال — ولا تؤكد تسليمها أو قراءتها. لذلك، عليك استهلاك webhook للأحداث الواردةمن Chakra، الذي يرسل تحديثات الحالة (sent, delivered, read, failed) فور حدوثها، مع ربطها بـ whatsappMessageId أو externalId المُعاد في استجابة الإرسال الأصلية.
معالج webhook مبسّط:
app.post("/webhooks/whatsapp", (req, res) => { const event = req.body; if (event.type === "message_status") { const { messageId, status, errorCode } = event.data; // status: sent | delivered | read | failed updateMessageRecord(messageId, { status, errorCode }); if (status === "failed" && errorCode) { handleWhatsAppResponse({ ok: false, json: async () => ({ error: { code: errorCode } }) }); } } res.sendStatus(200); // acknowledge quickly; don't block on downstream processing});إن تخزين whatsappMessageId إلى جانب السجل الخاص بك (مثل معرّف الطلب أو معرّف العميل المحتمل أو أيًا كان ما أطلق الإرسال) وقت الإرسال هو ما يجعل هذا webhook مفيدًا بالفعل — وإلا فستضطر إلى مطابقة أحداث الحالة مع سياقها بعد وقوعها.
الإرسال بكميات كبيرة: الالتزام بحد المعدل
عند 200 طلب في الدقيقة، ستصطدم حلقة مباشرة تمر على بضعة آلاف من المستلمين بأخطاء 429 بسرعة. ويمكن لنمط بسيط يعتمد على قائمة انتظار وتحديد المعدل أن يتعامل مع ذلك دون تعقيد مفرط:
async function sendBatch(recipients, templateName, buildMapping) { const RATE_LIMIT = 200; // calls per minute const DELAY_MS = Math.ceil(60000 / RATE_LIMIT); for (const recipient of recipients) { try { await sendTemplateByPhone(recipient.phone, templateName, buildMapping(recipient)); } catch (err) { console.error(`Send failed for ${recipient.phone}:`, err.message); // classification already handled inside handleWhatsAppResponse } await new Promise((resolve) => setTimeout(resolve, DELAY_MS)); }}لما يزيد على بضعة آلاف من الرسائل، يجدر إعداد قائمة انتظار مهام مناسبة (BullMQ أو SQS أو ما شابه) مع تراجع تدريجي عند إعادة المحاولة، بدلًا من استخدام حلقة داخل العملية — فهي تصمد أمام إعادة تشغيل العملية في منتصف الدفعة، بخلاف حلقة for البسيطة.
لماذا تبني هذا على Chakra
إليك بعض الأمور التي يجدر معرفتها عند تقييم المنصة التي ستبني عليها هذا التكامل:
- وثائق شاملة — مقتطفات برمجية وأمثلة للطلبات والاستجابات ودروس حول webhook لكل نقطة نهاية، حتى لا تضطر إلى استنتاج سلوك النظام بالتجربة والخطأ
- بنية تحتية للمؤسسات — وقت تشغيل بنسبة 99.9%، مع منطق مدمج لإعادة المحاولة ومعالجة حدود المعدل من جانب Chakra، وهو أمر مهم عند الإرسال بكميات كبيرة، إذ لا يمكنك تحمل فقدان الرسائل دون إشعار
- غلاف بسيط وشفاف — نقطة النهاية بتنسيق Meta ليست سوى تمرير مباشر فعلًا، لذا تظل وثائق Meta نفسها مرجعًا صالحًا بالكامل؛ ولن تضطر إلى تعلّم طبقة تجريد خاصة لكل حالة استخدام
- تسعير شفاف بلا هوامش إضافية — تدفع أسعار Meta القياسية على الرسائل القالبية، دون إضافة أي هامش خفي
- webhooks فورية — تتوفر الأحداث الواردة وتغييرات حالة التسليم فور حدوثها، ليتمكن نظامك من الاستجابة للإخفاقات (مثل الحالتين 131049 و131026 أعلاه) دون الحاجة إلى الاستقصاء الدوري
إذا كنت تبني نظام إشعارات يحتاج إلى التوسع ليتجاوز بضع رسائل اختبارية، فمن الأفضل استخدام API الخاصة بـ Chakra مباشرةً بدلًا من تجميع استدعاءات Meta Cloud API منخفضة المستوى بنفسك — فمنطق إعادة المحاولة ومعالجة حدود المعدل والإرسال المرتبط بـ CRM كلها متوفرة بالفعل.
احصل على مفتاح API وابدأ الإرسال — سجّل، واربط WABA، ويمكنك إرسال أول رسالة قالبية خلال دقائق.