مرجع API
POST /api/v1/send هي Send API للبريد المعاملاتي — نقطة نهاية واحدة مستقرة ذات إصدار. أما جهات الاتصال والصفقات والرحلات فراجع لها واجهة API للأتمتة، وللأحداث راجع Webhooks. وبخلاف بقية مسارات MailInApp (التي لا يستدعيها سوى واجهتنا الأمامية ويمكن تغييرها بحرية)، فهذه عقد تعتمد عليه شيفرة أطراف ثالثة، لذا فهي ذات إصدار منذ اليوم الأول.
المصادقة
Authorization: Bearer mia_live_...
المفتاح المفقود أو غير الصالح يعيد 401. وتُدار المفاتيح ضمن المطوّرون في لوحة التحكم — راجع البدء السريع. والمفتاح الملغى يتوقف عن العمل فورًا.
الطلب
Content-Type: application/json. يتشارك شكلان للطلب نقطة النهاية — ويُحدَّد أيّهما ينطبق بحسب وجود projectId من عدمه.
الإرسال الحر
| الحقل | النوع | مطلوب | ملاحظات |
|---|---|---|---|
to | string | نعم | عنوان مستلم واحد. |
subject | string | نعم | يُقتطع إلى 200 حرف. |
html | string | نعم | يُرسل كما هو — دون عرض، ودون وسوم دمج، ودون مسار الاستوديو. |
text | string | نعم | الجزء النصي العادي. |
القالب
| الحقل | النوع | مطلوب | ملاحظات |
|---|---|---|---|
to | string | نعم | عنوان مستلم واحد. |
projectId | string | نعم | يجب أن يكون مشروعًا تملكه — رابط الاستوديو هو /studio/<projectId>. |
mergeData | object | لا | كائن { key: value } مسطّح، بما يصل إلى 100 حقل. يحلّ محل صف مصدر بيانات — ويُحلّ في وسوم الدمج {{field}} الخاصة بالمشروع. تُسقط الكائنات/المصفوفات المتداخلة؛ وتصبح null/undefined سلاسل فارغة؛ ويُحوَّل كل ما عدا ذلك إلى سلسلة نصية. |
subject | string | لا | يكون افتراضيًا اسم المشروع إن حُذف. وتُحلّ وسوم الدمج في الموضوع من mergeData. ويُقتطع إلى 200 حرف. |
الجسم الذي لا يطابق أيًّا من الشكلين (مثل غياب to، أو غياب كلٍّ من html/text وprojectId) يعيد 400.
الحقول المشتركة
| الحقل | النوع | مطلوب | ملاحظات |
|---|---|---|---|
type | "transactional" | "marketing" | لا | القيمة الافتراضية "transactional". راجع أدناه. |
from | object | لا | تجاوز لعنوان المُرسِل/اسمه في استدعاء واحد — { "email": string, "name"?: string }. راجع أدناه. |
senderId | string | لا | تجاوز في استدعاء واحد باختيار إحدى هويات المُرسِل المحفوظة في حسابك بدلًا من كتابة from ضمن الطلب. يجب أن تنتمي إلى حسابك، وإلا فـ 400. ويُقدَّم from إن أُعطي الاثنان. |
replyTo | string | لا | عنوان Reply-To لاستدعاء واحد. راجع أدناه. |
الترويسات
| الترويسة | مطلوبة | ملاحظات |
|---|---|---|
Authorization | نعم | Bearer <apiKey>. |
Idempotency-Key | لا | راجع منع التكرار. |
الاستجابة
{ "id": "abc123", "status": "sent" }
تكون قيمة status إحدى ما يلي:
| الحالة | المعنى |
|---|---|
sent | سُلّم بنجاح إلى طريقة الإرسال لديك (خادم ترحيل SMTP أو الإرسال المباشر). |
suppressed | المستلم مدرج في قائمة حظر الإرسال لديك — راجع المعاملاتي مقابل التسويقي — أو أن العنوان لا يستطيع استقبال البريد، وعندها تتضمن الاستجابة أيضًا "reason": "invalid-address" (راجع التحقق من البريد). لم تُرسل أي رسالة؛ وهذا ليس خطأ. |
failed | فشلت محاولة الإرسال (مثلًا لأن خادم ترحيل SMTP غير مضبوط، أو رفض الرسالة). ويحمل حقل error سببًا مقروءًا. |
رموز الأخطاء
| الحالة | المعنى |
|---|---|
400 | JSON مشوّه، أو طلب لا يطابق شكل الإرسال الحر ولا شكل القالب، أو type غير صالح، أو from غير صالح أو غير مسموح (راجع عنوان المُرسِل)، أو replyTo غير صالح، أو (في وضع القالب) projectId غير موجود أو لا تملكه. ويُعاد أيضًا حين لا تكون للحساب طريقة إرسال عاملة مضبوطة. |
401 | ترويسة Authorization مفقودة أو غير صالحة، أو أن المفتاح أُلغي. |
404 | (في وضع القالب) المشروع غير موجود أو لا يملكه حسابك — وهي عمدًا الاستجابة نفسها لحالة «غير موجود» لتجنّب كشف معرّفات المشاريع الصالحة لحسابات أخرى. |
429 | تجاوز حدّ المعدّل — راجع حدود المعدّل. |
تُعاد 200 مع status: "failed" (بدلًا من حالة خارج نطاق 2xx) حين يكون الطلب نفسه صالحًا لكن محاولة الإرسال الفعلية فشلت لاحقًا. فإن احتجت إلى التمييز بين «رفضنا طلبك» و«حاولنا ولم تُرسل»، فافحص status في الجسم، لا رمز حالة HTTP وحده.
المعاملاتي مقابل التسويقي
يتحكم الحقل type في قائمة حظر الإرسال التي تُفحص، على غرار فصل مزوّدي خدمة البريد بين تدفقَي البريد المعاملاتي والتسويقي:
type: "transactional"(الافتراضي) — يتجاوز حظر الإرسال بسبب إلغاء الاشتراك. فإعادة تعيين كلمة المرور أو إيصال الطلب لا ينبغي أن يُحجب لمجرد أن المستلم ألغى اشتراكه في نشرتك الإخبارية. لكنه لا يتجاوز أبدًا حظر الإرسال بسبب الارتداد — فالعنوان الميت ميت بغض النظر عن النية.type: "marketing"— يتصرّف تمامًا كإرسال حملة من لوحة التحكم: يحجبه حظر الإرسال بسبب إلغاء الاشتراك وبسبب الارتداد معًا.
تعيد الحالتان status: "suppressed" بدلًا من خطأ عند الحجب.
عنوان المرسل
افتراضيًا يستخدم كل إرسال هوية المُرسِل المضبوطة في حسابك — بطاقة «عنوان المُرسِل» في الإعدادات ← النطاقات إن كنت تستخدم الإرسال المباشر (SES)، أو عنوان المُرسِل المضبوط في خادم ترحيل SMTP خلاف ذلك. مرّر from لتجاوزه في استدعاء واحد:
{
"to": "[email protected]",
"subject": "Your one-time code",
"html": "<p>Your code is 123456</p>",
"text": "Your code is 123456",
"from": { "email": "[email protected]", "name": "FitConsent Sales Team" }
}
الحقل from.email مطلوب متى وُجد from أصلًا — فلا يوجد تجاوز للاسم وحده، لذا فإن القيمة هنا تستبدل دائمًا العنوان واسم العرض كليهما في ذلك الاستدعاء. وfrom.name اختياري؛ احذفه لترسل بالعنوان المجرد فقط.
إن كان حسابك يرسل عبر نطاق موثّق (الإرسال المباشر/SES)، فيجب أن يكون from.email عنوانًا على أحد نطاقات الإرسال الموثّقة الخاصة بك (مثل [email protected]، لا [email protected]) — فالحساب يستطيع توثيق أكثر من نطاق، فأيٌّ منها مسموح، لكن لا يُسمح أبدًا بنطاق يخص غيرك. فالإرسال المباشر يعمل على حساب AWS واحد للمنصة يتشاركه كل عملاء MailInApp، وهذا القيد هو ما يمنع حسابًا من إرسال بريد يبدو صادرًا عن النطاق الموثّق لحساب آخر. أما في الإرسال عبر SMTP فلا يوجد قيد كهذا: إذ يمر الإرسال عبر خادم الترحيل وبيانات الاعتماد الخاصة بك، فهو موثوق أصلًا بالطريقة نفسها التي تعمل بها قواعد التحقق من المُرسِل في خادم الترحيل لديك.
عادةً ما يكون senderId (راجع جدول الحقول المشتركة أعلاه) الخيار الأبسط حين تكون قد أعددت هوية مُرسِل في لوحة التحكم مسبقًا. فهو يُحلّ إلى {email, name, reply-to} الخاصة بتلك الهوية دون أن تضطر إلى تكرارها في كل استدعاء.
قيمة from غير الصالحة أو غير المسموح بها تعيد 400 قبل أي محاولة إرسال.
عنوان الرد (Reply-To)
افتراضيًا، تذهب الردود إلى عنوان Reply-To الافتراضي المضبوط في حسابك، والذي يُعيَّن في صفحة الإرسال أو النطاقات ضمن الإعدادات بحسب طريقة الإرسال لديك، أو لا تذهب إلى أي مكان إن لم تعيّنه. مرّر replyTo لتجاوزه في استدعاء واحد — وهو مفيد حين يكون from الظاهر عنوانًا لا يستقبل ردودًا لكنك لا تزال تريد أن يرى إنسان الردود:
{
"to": "[email protected]",
"subject": "Your order shipped",
"html": "<p>Your order is on its way.</p>",
"text": "Your order is on its way.",
"from": { "email": "[email protected]", "name": "FitConsent" },
"replyTo": "[email protected]"
}
وبخلاف from.email، لا يخضع replyTo لأي قيد على النطاق في الإرسال المباشر (SES) — فهو لا يؤثر أبدًا في هوية الإرسال أو سمعة قابلية التسليم، بل مجرد ترويسة يلتزم بها تطبيق بريد المستلم حين يضغط «رد». وقيمة replyTo غير الصالحة تعيد 400 قبل أي محاولة إرسال.
منع التكرار (Idempotency)
مرّر ترويسة Idempotency-Key في أي استدعاء قد تُعاد محاولته — إعادة تسليم Webhook دفع، أو مستهلك طابور يعيد معالجة رسالة. فالاستدعاء المعاد بالمفتاح نفسه على الحساب نفسه يعيد {id, status} الخاصين بالاستدعاء الأصلي دون إرسال رسالة ثانية، حتى لو كان الاستدعاء الأول لا يزال قيد التنفيذ.
المفاتيح مقيّدة بكل حساب وليس لها انتهاء صلاحية؛ وتجنّب إعادة استخدام مفتاح سبق أن استخدمته لحمولة مختلفة مسؤوليتك أنت (إذ سيعيد نتيجة الاستدعاء الأول، لا إرسال الحمولة الجديدة). وإن لم تمرّر مفتاحًا، فكل استدعاء يرسل.
حدود المعدّل
تنطبق ثلاثة حدود مستقلة:
- لكل مفتاح API: حدّ لعدد الطلبات في الدقيقة. وتجاوزه يعيد
429لذلك المفتاح تحديدًا — دون أن تتأثر المفاتيح الأخرى في الحساب نفسه. - لكل حساب، حصة Send API: تتضمن باقتك عددًا من استدعاءات Send API في كل شهر تقويمي (500/شهر في الباقة المجانية). وتجاوزه يعيد
429حتى تُعاد تعيين الحصة في اليوم 1 من الشهر. - لكل حساب، حجم الإرسال: الحدّ الأقصى الشهري التراكمي لحجم الإرسال في باقتك، المشترك بين كل مسارات الإرسال (الإرسال من لوحة التحكم، والإرسال المجدول، ورسائل دورة الحياة، وهذه الواجهة). وهو الحدّ نفسه الذي يحمي بقية عمليات الإرسال في حسابك — فلا تحصل Send API على ميزانية منفصلة.
تفاصيل وضع القالب
يعرض الإرسال في وضع القالب المشروع المرتبط تمامًا كإرسال عادي إلى مستلم: مستويات محرك البدائل الثلاثة، والعناصر التفاعلية، ورابط عرض مباشر شخصي موقّع مبني من mergeData بدلًا من صف جهة اتصال مخزّن. ويتصرّف كل ما يلي ذلك كإرسال من الاستوديو:
- تُسجَّل أصوات الاستطلاعات والتقييمات وإرسالات النماذج ومرات الفتح وتظهر في عرض الردود للمشروع، منسوبةً إلى استدعاء API هذا تحديدًا بدلًا من صف جهة اتصال.
- يُطلق Webhook مشروعك مع كل حدث تفاعل، كأي مستلم آخر.
- تُدرج الاستدعاءات الأخيرة (الحالة، والمستلم، والطابع الزمني) في صفحة المطوّرون في لوحة التحكم سجلًّا للتدقيق.
والأمر الوحيد المختلف عن الإرسال من لوحة التحكم: لا يوجد صف مصدر بيانات مخزّن وراء التفاعل، لذا ينبغي أن تعتمد عمليات الربط من جهتك على معرّف المستلم في الرد بدلًا من فهرس الصف.