دورة حياة التسليم
كل رسالة صادرة ترسلها عبر Orbit تحمل حقلstatus يتقدم بينما تنتقل الرسالة من استدعاءك للواجهة البرمجية نحو جهاز المستلم — أو نحو نتيجة فاشلة. تشرح هذه الصفحة هذه الآلة الحالة المفهومية: ما تعنيه الحالات، ما الذي يدفع كل انتقال، وأين توجد الحواف الخاصة بالمشغّل. اقرأها قبل اشتراكك في أول webhook لك أو تفرّع تكاملك على نتيجة الرسائل.
تعيش دلالات الحالة، جدول الانتقالات الكامل، وخريطة حدث webhook في مرجع دورة حياة حالة الرسائل؛ مخطط الاستجابة لقراءة الحالة الحالية للرسالة موجود في مرجع واجهة API للمراسلة. هذه الصفحة تربط بينهما بمستوى أعلى.
المسار السعيد
رسالة تنجح من طرف إلى طرف تمر عبر:pending → queued → sending → sent → delivered → read
كل انتقال يُدفَع بواسطة جهاز مختلف — لا طرف واحد يرى المسار كله:
جهازان يجلسان بجانب هذا المسار ويمكنهما إخراج سجل منه:
- جدولة no-DLR. عندما لا يُرجع مشغّل إيصالاً للتسليم على الإطلاق، تروّخ الجدولة
sentإلىsubmitted_no_receiptبعد نافذة سماح خاصة بالقناة — راجع المؤكد من المشغّل مقابل. وسيط أولي على السلك. - أنت (المشغّل). إلغاء رسالة مجدوّلة لم تُرسَل أذّ بها إلى
cancelled، حالة نهائية لا يلمسها أي استدعاء للمقدم ولا أي DLR. تُحل مرسلات الصندوق إلىtest_sent، حالة نهائية يُبلّغ إليها قبل أي نشر للمقدم. حذف سجل ينقل أي حالة نهائية إلىdeleted، بعدئذ لا شيء يمكن أن يلمسه.
scheduled حتى وقت الإطلاق ثم تنضم إلى قائمة الانتظار. لا يمكن أن تغادر scheduled إلا ثلاث طرق: ترقيتها إلى queued عند وقت الإطلاق، cancelled منك، أو expired إذا انتهت نافذة صلاحيتها قبل النشر.
التحقق من المشغّل مقابل. وسيط أولي على السلك
أهم تمييز للتقارير والتسوية هو ما إذا كانت الحالة نتيجة مؤكَّدة من المشغّل أم حاربها القبّيل على السلك:deliveredوreadمؤكَّدان من المشغّل. llegó DLR حقيقي؛ المشغّل نفسه اكتفى بالنتيجة.submitted_no_receiptحاربه أولي. يعني “قُبِلت الإرسال، ولم يُعاد أي إيصال ضمن نافذة السماح.” يُوصَف أنه أولي (state_class: "intermediate",is_terminal: false) على كل webhook، لأن DLRdeliveredأوreadأو فاشل حقيقي يمكن أن يصل لاحقًا ويستبدله.
submitted_no_receipt على أنه نتيجة مجهولة، لا كتسليم. ما إذا كان “مجهول” يميل إلى إيجابي أو مجرد محوّل يعتمد على القناة — راجع تنوعات القناة.
expired هو النظر عن الجانب الآخر: llegó DLR، لكن للغاية متاخرًا بحيث أن نافذة الإيصال كانت قد أُغلقَت. لا يمكن معرفة النتيجة والسجل مغلق؛ expired يُوزَّع على المشتركين كحدث message.failed.
تنـوعات القناة
- قنوات DM من Meta (Instagram, Messenger). واجهة الإرسال الخاصة بـ Meta لا تصدر إيصالات التسليم أبدًا. يغادر الإرسال Orbit كـ
sent، يعلمزno_dlr_channelفي متطافات الرسالة، ويتحول إلىsubmitted_no_receiptبعد 5 دقائق. في مستلم تم مقبول اختيارياً، يضمن Meta التسليم على القبول — لذلك في هذه القنوات يتصرفsubmitted_no_receiptمثل إشارة وظيفية للتسليم، ومتطافات الرسالة تحملno_dlr_channel: trueلتستطيع التمييز بين الحالة. - قنوات تدعمها SMPP (SMS, MMS, صوت، فاكس، RCS). نافذة السماح 30 دقيقة. هنا
submitted_no_receiptهو محوّل حقيقي: قد يكون الجهاز قد تلقّى الرسالة بدون إيصال تم الإبلاغ عنه، قد لا يُرسل المشغّل إيصالات أبدًا على ذلك الطريق، أو قد يكون الإيصال قد أُسقِطَ في العبور. تتبَّع هذه النسبة على إشارة منفصلة عن نسبة التسلميات — نسبةsubmitted_no_receiptالمترسّخة على وجهة واحدة تُشير إلى طريق غير متوَلِيّ أو إلى طريق إيصال مكسور، وهي تستحق التحقيق في أيّ من الحالتين. - البريد الإلكتروني. يضيف نتيجة فاشلة لا يملكها غار Channels:
bounced، عندما يرفُض خادم البريد الاستقبال الرسالة. عدد الارتداد ضد معدل الفشل النهائي الخاص بك مثلfailed، لكنه حالة مفروقَة لتمكينك من فصل الرفضات من جانب المستلم عن تلك الجانب المقدم. - إلغاء المشغّل.
cancelledيُبلّغ إليها فقط من أنت — عبرPOST /messages/:id/cancelعلى رسالة لم تُرسَل. لا مشغّل يكتبها أبدًا، ونتيجةً لا تطلق أي حدث webhook؛ لا شيء يُخبِر المشترك عن الإلغاء. استشِرGET /messages/:idإذا كنت تُعرِّض الإلغاء في واجهتك وتحتاج إلى مراقبته. - الصندوق / وضع الاختبار. مرسلات الاختبار تحل إلى
test_sentقبل أي نشر للمقدم. تطلقmessage.sentمعstatus: "test_sent"وmetadata.test_mode: true، لذا المشترك يجب أن يُفرّع علىmetadata.test_modeليبقي حركة الصندوق خارج معالجة الإنتاج.
ما يُفرّع عنه
يجب أن تتبدأ التكاملات على الحقول القابلة للقراءة آلياً فقط، أبدًا على الأوصاف العرضية:status— الحالة الحالية للرسالة. هذه نقطة التفرّع الأولية. تعامل مع المجموعة كاملة: غير حالات المسار السعيد والنتيجة المتفقّلة، لا تنسcancelled،test_sent،submitted_no_receipt، وbounced.metadata.classified_error_code— موجود على النتائج النهائية الفاشلة؛ الفئة الفاشلة المُعرَّفة آلياً والمُمَكِّنَة آلة. ادمِجها معerror_code/error_messageخام على webhookmessage.failedعندما تريد كلمات المشغّل.metadata.no_dlr_channel— موجود على سجلات DM من Meta؛ يخبرك أنsubmitted_no_receiptهو حالة التسليم المضمونة من Meta، لا الحالة المحوّلة من SMPP.state_class/is_terminal— على webhook الحياة.is_terminal: true(بالم زوجةstate_class: "terminal") يعني أن النتيجة نهائية؛submitted_no_receiptتبلغis_terminal: falseبالضبط لكي لا تغلِق الكتاب عليها.
أخطاء شائعة
- تعامل
message.createdعلى القبول.message.createdيطلق في اللحظة التي توضع السجل في قائمة الانتظار، قبل أي استدعاء للمقدم. رفض متزامن (4xx على الإرسال، أوrejected/failedفوري) يترك ذلك الحدث مسلَّم. اجمِعه معmessage.sentقبل أن تستنتج أن الرسالة خرجت. - تفترض أن
deliveredغير قابلة للتغيّر. المشغّلون على الطرق بعض يصدِرون إيصالًا للتسليم، ثم تصحيحًا بعد دقائق — بعض المشغّلين الهنديين والبرازيليين يفعلون ذلك. Orbit يحترم ذلك: يمكن للسجل أن ينتقل منdelivered → undeliveredأو منdelivered → failed. إذا ربَّيت الحالات في مخزن بياناتك الخاص، طبِّق التحديثات بشكل عدم تثويري حسب معرف الرسالة بدلاً من تجاهل الانتقالات لرسالة وعلها من قبل. - تنتظر webhook على
cancelled. لن يأتي أبدًا — الإلغاء يرتَبط باستدعاءك للواجهة البرمجية، لا باستدعاء المشغّل، لذا المنصة لا تصدر حدثًا له. السجل يجلس ببساطة فيcancelledحتى تحذِفه. - ترمي
submitted_no_receiptفي مجرى الفشل. يصل على نوع حدثmessage.failedلأسباب التعَبب (لا يوجد حدود مخصص)، لكن حذرن يكتو حولdata.status، لا نوع الحدث، واحصِر بها خارج مقاعع الفشل الثابت. - تتوقع حدثًا نهائيًا واحدًا لكل رسالة. قد تصدر رسالة
message.failedمعstatus: "submitted_no_receipt"ثمmessage.delivered، عندما يصل إيصال المشغّل البطيء أخيرًا ضمن نافذة الوصول المتراخَى. إحدِّثه وجهّزه بواسطةmessage_id، ودَع الحدث المتأخر يغلب.