تخطَّ إلى المستندات
PHP

إرسال دفعة

`emails->sendBatch`: حتى 100 رسالة، ونتائج لكل عنصر.

emails->sendBatch

send_batch.php
$invoices = [    ['number' => 'INV-1042', 'email' => '[email protected]'],    ['number' => 'INV-1043', 'email' => '[email protected]'],]; $messages = []; foreach ($invoices as $invoice) {    $messages[] = [        'from' => '[email protected]',        'to' => $invoice['email'],        'subject' => 'Invoice ' . $invoice['number'],        'text' => 'Your invoice is attached.',    ];} $result = $client->emails->sendBatch($messages, idempotencyKey: 'invoices:2026-09'); echo $result->sent, ' sent, ', $result->failed, ' failed', PHP_EOL; foreach ($result as $item) {    if ($item['status'] === 'error') {        error_log($item['index'] . ' ' . $item['error']['code'] . ' ' . $item['error']['message']);    } else {        echo $item['index'], ' ', $item['email']['id'], PHP_EOL;    }}

يأخذ sendBatch قائمة من مصفوفات الرسائل، كل منها بشكل المصفوفة التي يأخذها emails->send تمامًا، ويعيد OpenEmail\Result\BatchResult. ويحمل items الخاص به مصفوفة واحدة لكل رسالة، بالترتيب، وكل منها إما ok مع رسالته وإما error مع الغلاف الذي كانت تلك الرسالة ستُرفض به، والمرور على النتيجة في حلقة يمر عليها. ولا يُتراجع عن شيء، فعدد failed فوق 0 قائمة يجب التصرف بشأنها لا سبب لإعادة إرسال الدفعة.

مفتاح واحد للاتكرارية يغطي الدفعة كلها ويوسّعه الخادم لكل عنصر، فالدفعة التي تُعاد محاولتها تعيد تشغيل كل رسالة بدل أن تطويها كلها على الرسالة الأولى. أرسل القائمة نفسها بالترتيب نفسه حين تعيد المحاولة: فالعنصر الذي غيّر موضعه يرتبط بمفتاح موضع آخر ويعود بخطأ idempotency_key_reuse.

الرسالة المرفوضة لا ترمي استثناءً. وحدها المشكلة في الدفعة ككل ترمي استثناءً: قائمة فارغة، أو أكثر من 100 رسالة، أو أكثر من 10 تحمل translate، أو إخفاق في المفتاح أو النطاق، أو عطل في الخادم. والعطل في الخادم في منتصف الطريق يأتي بعد أن تكون العناصر الأولى قد ذهبت، فيعيد العميل المحاولة بالمفتاح نفسه، مما يعيد تشغيل تلك العناصر بدل إرسالها مرتين.

تُرسل العناصر واحدًا تلو الآخر داخل طلب واحد، فالدفعة الكبيرة من الإرسالات الفورية تستغرق وقتًا أطول بوضوح من send واحد. اجعل timeout: الخاص بالعميل سخيًا.

المعاملات: emails->sendBatch

emailsarrayمطلوب
من رسالة واحدة إلى 100، تُرسل بالشكل `{"emails": [...]}` وتُقبل واحدة تلو الأخرى بالترتيب المعطى. وتمر كل منها بالمعالجة نفسها التي يجريها `emails->send`، فيُغلَّف المستلم المنفرد، ويصبح `DateTimeInterface` لحظة زمنية، وتُرمَّز بايتات المرفقات، والعنصر الذي ليس مصفوفة يرمي `InvalidArgumentException` قبل إرسال أي شيء. والقائمة الفارغة، أو أكثر من 100، أو أكثر من 10 رسائل تحمل `translate`، ترفض الاستدعاء كله بـ `validation_error` على `emails`. وكذلك يرفض الاستدعاءَ كلَّه غيابُ النطاق `emails:send` و`idempotencyKey:` المشوّه، قبل إرسال رسالة واحدة.
idempotencyKeystring
يمنع تكرار الدفعة عبر العمليات المختلفة. ويرفق العميل مفتاحًا مولَّدًا حديثًا مع كل استدعاء على أي حال، فإعادات محاولته لا تُرسل مرتين أبدًا، ويوسّع الخادم أي مفتاح يصله لكل عنصر بالشكل `key/0` و`key/1` وهكذا، مفصولًا بشرطة مائلة، وهي محرف لا يجوز أن يحتويه مفتاحك، فلا يمكن لمفتاح واحد على مئة رسالة أن يطويها على الأولى.
apiKeystring
يرسل الدفعة بهذا المفتاح بدل مفتاح العميل.

كل رسالة في emails

fromstring or arrayمطلوب
المُرسِل، في صورة عنوان مجرّد أو `Name <addr@host>` أو مصفوفة فيها `email` و`name`. لا يوجد مُرسِل احتياطي، ويجب أن يكون هذا العنوان مسموحًا للمفتاح. والرفض يُفشل ذلك العنصر وحده، في صورة `permission_error` بالرمز `from_address_forbidden`.
tostring or arrayمطلوب
مستلم واحد على الأقل، والمستلم المفرد يلفّه العميل داخل قائمة. وبحد أقصى 50 عنوانًا عبر `to` و`cc` و`bcc` مجتمعة، تُحسب لكل رسالة لا عبر الدفعة.
ccstring or array
الافتراضي لا أحد، وتُحسب ضمن إجمالي الخمسين عنوانًا نفسه مع `to` و`bcc`.
bccstring or array
الافتراضي لا أحد، وتُحسب ضمن إجمالي الخمسين عنوانًا نفسه. و`Bcc` من الأسماء التي لا يجوز لـ `headers` ضبطها، فهذه هي الطريقة الوحيدة للنسخ المخفي. إذ إن صيغة الترويسة كانت ستنقض المُغلَّف المنفصل لكل مستلم وهو ما يبقي العنوان مخفيًا.
replyTostring or array
إلى أين تذهب الردود. يُطبَّق بعد `headers`، فيطمس `Reply-To` الذي ضبطته هناك أيضًا بدلًا من إضافة ترويسة ثانية.
subjectstring
بحد أقصى 998 حرفًا، وهو حد السطر في RFC 5322، والافتراضي سلسلة نصية فارغة. والموضوع الفارغ يُستعاض عنه بموضوع القالب نفسه حين يوفّر `template` موضوعًا.
htmlstring
جزء HTML، بحد أقصى مليون حرف، وهو الجزء الذي يراه المستلمون عندما يُعطى الجسمان معًا. ويلزم واحد من `html` أو `text` أو `template` أو `draftId`، والعنصر الخالي منها جميعًا يفشل بـ `validation_error` على `html`.
textstring
جزء النص العادي، بحد أقصى مليون حرف. ويمكن إرسال الاثنين، وكل وسيلة نقل على هذا المسار تبني جسمًا واحدًا من سلسلة واحدة، فيغلب `html` حيث يوجد.
headersarray
`X-*` و`List-*` وReply-To وPrecedence وAuto-Submitted وImportance وPriority وFeedback-ID فقط. وأي ترويسة يضبطها النقل بنفسه (From وTo وBcc وSubject وMessage-ID وترويسات DKIM وARC) تُرفض بوصفها `reserved_header` بدل أن تُحذف بصمت. والقيم بحد أقصى 998 حرفًا ولا يجوز أن تحمل CR أو LF أو NUL، لأن السطر الثاني ترويسة ثانية.
attachmentsarray
بحد أقصى 20 ملفًا لكل رسالة، مع حد 5 ميغابايت للملفات المضمّنة مجتمعة بعد فك الترميز، يُحسب لكل رسالة لا لكل دفعة. ويكون `content` بترميز base64 عبر الشبكة. مرّر تدفقًا من `fopen` أو `SplFileInfo` أو تدفق PSR-7 فيقرؤه العميل ويرمّزه، أو سلسلة نصية بترميز base64 مسبقًا. والمصفوفة التي فيها `fileId` وحده تسمّي ملفًا موجودًا بالفعل في مساحة العمل ولا تُحتسب ضمن حد الملفات المضمّنة.
threadIdstring
الرد داخل محادثة قائمة، بحد أقصى 256 حرفًا. ويكتب النقل منه ترويستي In-Reply-To وReferences، وهو ما يجعل الرد يحط داخل المحادثة لا بجانبها.
draftIdstring
أرسل محتوى مسودة محفوظة تحت هذا المُغلَّف، بحد أقصى 256 حرفًا. والمستلمون والموضوع والترويسات المبنية هنا هي ما يخرج عبر الشبكة.
templatearray
اعرض قالبًا مخزَّنًا على الخادم، بالمعرّف (`tpl_…`) أو بالـ slug، مع تثبيت `version` لمراجعة بعينها وملء `props` و`slots` له. يُحسم مرة واحدة، عند قبول العنصر، ويُرفض مع `html` أو `text` ومع `draftId`، لأن كلًّا منها جواب ثانٍ عن محتوى الرسالة.
scheduledAtDateTimeInterface or string
`DateTimeInterface`، أو لحظة ISO 8601، أو مدة مثل `PT1H`، بعد ثانية واحدة على الأقل في المستقبل و365 يومًا على الأكثر. وسلسلة التاريخ التي لا وقت فيها تعني منتصف الليل UTC في ذلك اليوم. وتُجدوَل العناصر باستقلال، فيمكن أن تضم دفعة واحدة مئة موعد إرسال مختلف.
cancellableForSecondsint
نافذة تراجع بالثواني على إرسال فوري، من 0 إلى 900، والافتراضي 0. وأي قيمة فوق 0 تُرفض مع `scheduledAt` على العنصر نفسه، لأن الرسالة المجدولة قابلة للإلغاء أصلًا حتى تنطلق.
trackingarray
`opens` و`clicks`، كل منهما اختياري ويتجاوز الإعداد لهذه الرسالة وحدها. والمفتاح الذي تتركه يتبع العنوان الذي تُرسل منه الرسالة (أو الالتقاط الشامل الذي التقطها)، وهو معطَّل ما لم يفعّله ذلك العنوان.
tagsarray
بحد أقصى 10 وسوم، بمفاتيح من 1 إلى 64 حرفًا من المجموعة `A-Za-z0-9_-` وقيم حتى 256 حرفًا. تُعاد كما هي على الرسالة ولا تُفسَّر أبدًا: يرشّح `emails->list` حسب `status:` و`from:` و`broadcastId:` ونافذة الجدولة لا غير، فالوسم شيء تقرؤه من رسالة تملكها بالفعل لا وسيلة للعثور عليها.
translatearray
أرسل هذا العنصر بلغة أخرى، ويُحسم ذلك عند القبول فتكون الكلمات التي اعتُمدت هي الكلمات التي تخرج. ولا يجوز أن يحمله أكثر من 10 عناصر في دفعة واحدة: فكل عنصر ينفق عدة استدعاءات للنموذج والعناصر تُنفَّذ بالترتيب، فدفعة أكبر كانت ستُقطع في منتصف الإرسال. وفوق ذلك يُرفض الاستدعاء كله بـ `too_many_items` على `emails`، قبل إرسال أي شيء.

الاستجابة: OpenEmail\Result\BatchResult

النتيجة للقراءة فقط، وهي IteratorAggregate على items وCountable، فيمر foreach ($result as $item) على العناصر ويعدّها count($result).

itemsarray
مصفوفة واحدة لكل رسالة، بالترتيب الذي أرسلتها به. لا يُتراجع عن شيء، فهذا سجل لما حدث لكل رسالة لا تقرير عن معاملة. وتجيب API بـ 207 سواء قُبلت كل الرسائل أو بعضها أو لا شيء منها، فيعود الاستدعاء في كل الأحوال، و`status` الخاص بكل عنصر هو ما يجب التفرّع عليه.
sentint or null
كم عنصرًا قُبل، وهو ليس نفسه كم عنصرًا خرج. فقد يكون العنصر `ok` ومع ذلك يحمل `email` قيمة `status` فيه `failed` أو `partial`، لأن وسيلة نقل ترفض الرسالة بعد وجود السجل تمثل نتيجة تسليم لا طلبًا مرفوضًا. ويكون null فقط حين لا يحمل الرد أي عدد.
failedint or null
كم عنصرًا يحمل `error`. والعدد فوق 0 قائمة يجب التصرف بشأنها لا سبب لإعادة إرسال الدفعة. فالرسائل المقبولة قد ذهبت بالفعل.

كل عنصر

indexint
الموضع الذي احتلته رسالة هذا العنصر في القائمة التي أرسلتها. يُحمل كمفتاح إضافة إلى الترتيب، فتستطيع الشيفرة التي ترشّح `items` أو ترتّبها أن تعرف مع ذلك أي رسالة فشلت.
statusstring
`ok` أو `error`. يحمل `ok` الحقل `email`، ويحمل `error` الحقل `error`، ولا يحمل أي عنصر الاثنين.
emailarray
الرسالة المقبولة، على عنصر `ok` فقط، بالشكل نفسه الذي يعيده إرسال واحد. وتكون قيمة `replayed` فيها true حين يطابق `Idempotency-Key` المشتق إرسالًا موجودًا بالفعل، فلم يُرسَل شيء جديد وهذه هي الرسالة الأصلية. ولا تحمل المفتاح `tracking`، لأن التفاعل يُبلَّغ عنه لاحقًا ولا يوجد ما يُبلَّغ عنه عند القبول.
errorarray
لماذا رُفضت هذه الرسالة وحدها، على عنصر `error` فقط. وهو غلاف الخطأ الخاص بـ API منقوصًا منه `docUrl` و`requestId`: فهذان يصفان الطلب، والطلب ككل قد نجح.

خطأ العنصر

typestring
الفئة التي يُتفرّع عليها: `validation_error` و`permission_error` و`not_found_error` و`conflict_error` وغيرها. والمجموعة مجمَّدة ولن تكبر، بخلاف `code`.
codestring
الفشل المحدد: `from_address_forbidden` و`invalid_email_address` و`too_many_recipients` و`reserved_header` و`message_too_large` و`unknown_parameter`. مفتوحة وقابلة للإضافة، فعامل أي رمز لا تعرفه معاملة `type` الخاص به. واسمه هنا `code` لأن هذا هو الغلاف بعد فك ترميزه، في حين يحمل الاستثناء القيمة نفسها في `errorCode`.
messagestring
جملة واحدة مكتوبة لإنسان، تسمي القيمة المخالفة حيث توجد واحدة. وليست معرّفًا ثابتًا. فرّع على `code`.
paramstring
الحقل الذي رُفض، كمسار منقوط داخل تلك الرسالة: `to.0` أو `from` أو `attachments`. وغائب عندما لا يسمي الفشل أي حقل، ولا يُسبق أبدًا بموضع العنصر في الدفعة، فذاك ما يفيده `index`.