پرش به مستندات
Ruby

ارسال دسته‌ای

`emails.send_batch`: تا 100 پیام، با نتیجه به ازای هر مورد.

emails.send_batch

send_batch.rb
invoices = [  {number: "INV-1042", email: "[email protected]"},  {number: "INV-1043", email: "[email protected]"}] messages = invoices.map do |invoice|  {from: "[email protected]", to: invoice[:email], subject: "Invoice #{invoice[:number]}", text: "Your invoice is attached."}end result = client.emails.send_batch(messages, idempotency_key: "invoices:2026-09") puts "#{result.sent} sent, #{result.failed} failed" result.items.each do |item|  if item[:status] == "error"    warn "#{item[:index]} #{item.dig(:error, :code)} #{item.dig(:error, :message)}"  else    puts "#{item[:index]} #{item.dig(:email, :id)}"  endend

send_batch یک Array از Hashهای پیام می‌گیرد که هرکدام دقیقاً هم‌شکل بدنهٔ emails.send است، و یک OpenEmail::BatchResult برمی‌گرداند. items آن برای هر پیام یک Hash دارد، به همان ترتیب، که هرکدام یا ok است با پیامش، یا error است با پاکتی که آن پیام با آن رد می‌شد. هیچ‌چیز بازگردانده نمی‌شود، پس شمار failed بالاتر از 0 فهرستی برای اقدام است نه دلیلی برای فرستادن دوبارهٔ دسته.

یک کلید idempotency کل دسته را پوشش می‌دهد و سرور آن را برای هر مورد گسترش می‌دهد، پس دسته‌ای که دوباره تلاش شود هر پیام را بازپخش می‌کند، نه اینکه همه را روی اولی جمع کند. وقتی دوباره تلاشش می‌کنید همان Array را به همان ترتیب بفرستید: موردی که جابه‌جا شده به کلید جایگاه دیگری گره می‌خورد و به‌صورت خطای idempotency_key_reuse برمی‌گردد.

پیامی که رد شود خطا raise نمی‌کند. فقط مشکلی با کل دسته raise می‌شود: یک Array خالی، بیش از 100 پیام، بیش از 10 پیام دارای translate، شکست کلید یا اسکوپ، یا خطای سرور. خطای سرور در میانهٔ کار پس از رفتن موردهای پیشین رخ می‌دهد، و کلاینت با همان کلید دوباره تلاشش می‌کند، که آن موردها را به‌جای دو بار فرستادن، بازپخش می‌کند.

موردها یکی پس از دیگری درون یک درخواست فرستاده می‌شوند، پس یک دستهٔ بزرگ از ارسال‌های فوری به‌طور محسوسی بیشتر از یک send طول می‌کشد. timeout: کلاینت را سخاوتمندانه تنظیم کنید.

پارامترها: emails.send_batch

emailsArray<Hash>الزامی
یک تا 100 پیام، که به شکل `{"emails": [...]}` فرستاده می‌شوند و یکی‌یکی به همان ترتیب داده‌شده پذیرفته می‌شوند. هرکدام همان پردازشی را می‌گذراند که `emails.send` دارد، پس گیرندهٔ تنها در Array پیچیده می‌شود، Time به یک لحظه تبدیل می‌شود و بایت‌های پیوست رمزگذاری می‌شوند. یک Array خالی، بیش از 100 پیام، یا بیش از 10 پیام دارای `translate`، کل فراخوانی را با یک `validation_error` روی `emails` رد می‌کند. نبود اسکوپ `emails:send` و `idempotency_key:` بدشکل هم کل فراخوانی را رد می‌کنند، پیش از آنکه حتی یک پیام فرستاده شود.
idempotency_keyString
دسته را در میان فرایندها یکتاسازی می‌کند. کلاینت در هر حال روی هر فراخوانی کلیدی تازه‌ساخته می‌چسباند، پس تلاش‌های دوبارهٔ خودش هرگز دو بار نمی‌فرستند، و سرور هر کلیدی را که بگیرد برای هر مورد به شکل `key/0`، `key/1` و همین‌طور ادامه گسترش می‌دهد، جداشده با اسلش، نویسه‌ای که کلید خودِ شما اجازهٔ داشتنش را ندارد، تا یک کلید روی صد پیام نتواند همه را روی اولی جمع کند.
api_keyString
دسته را به‌جای کلید کلاینت با این کلید می‌فرستد.

هر پیام در emails

fromString or Hashالزامی
فرستنده، به شکل نشانی خام، `Name <addr@host>` یا یک Hash با `email` و `name`. هیچ فرستندهٔ جایگزینی وجود ندارد و کلید باید اجازهٔ این نشانی را داشته باشد. رد شدن فقط همان یک مورد را شکست می‌دهد، به شکل یک `permission_error` با کد `from_address_forbidden`.
toString, Hash or Arrayالزامی
دست‌کم یک گیرنده، و اگر یکی تنها بدهید کلاینت آن را در یک Array می‌پیچد. حداکثر 50 نشانی روی هم در `to`، `cc` و `bcc`، که به ازای هر پیام شمرده می‌شود نه در کل دسته.
ccString, Hash or Array
پیش‌فرض: هیچ، و به همان سقف 50 نشانی شمرده می‌شود که `to` و `bcc` هم به آن شمرده می‌شوند.
bccString, Hash or Array
پیش‌فرض: هیچ، و به همان سقف 50 نشانی شمرده می‌شود. `Bcc` یکی از نام‌هایی است که `headers` اجازهٔ تنظیمش را ندارد، پس این تنها راه رونوشت پنهان است. شکل هدری، همان پاکتِ به‌ازای‌هر‌گیرنده را که نشانی را پنهان نگه می‌دارد از بین می‌برد.
replyToString or Hash
جایی که پاسخ‌ها می‌روند. پس از `headers` اعمال می‌شود، پس `Reply-To`ای را که آنجا هم گذاشته باشید بازنویسی می‌کند، نه اینکه دومی اضافه کند.
subjectString
حداکثر 998 نویسه، همان حد طول خط RFC 5322، با پیش‌فرض یک String خالی. موضوع خالی، وقتی `template` موضوعی داشته باشد، به موضوع خودِ قالب می‌افتد.
htmlString
بخش HTML، حداکثر یک میلیون نویسه، و همان بخشی که وقتی هر دو بدنه داده شوند گیرندگان می‌بینند. یکی از `html`، `text`، `template` یا `draftId` الزامی است، و موردی که هیچ‌کدام را نداشته باشد با یک `validation_error` روی `html` شکست می‌خورد.
textString
بخش متن ساده، حداکثر یک میلیون نویسه. هر دو را می‌شود فرستاد، و هر لایهٔ انتقالی در این مسیر از یک String یک بدنه می‌سازد، پس هرجا `html` باشد همان برنده است.
headersHash
فقط `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<Hash>
حداکثر 20 فایل به ازای هر پیام، با مجموع 5 MB برای فایل‌های درون‌خطی پس از رمزگشایی، که به ازای هر پیام شمرده می‌شود نه به ازای دسته. `content` روی سیم base64 است. بایت‌ها را به‌صورت یک String دودویی، یک IO یا یک Pathname بدهید تا کلاینت رمزگذاری‌شان کند. یک Hash فقط با `fileId` فایلی را نام می‌برد که از پیش در فضای کاری هست و در سقف درون‌خطی شمرده نمی‌شود.
threadIdString
پاسخ‌دادن درون یک thread موجود، حداکثر 256 نویسه. حمل‌ونقل In-Reply-To و References را از روی آن می‌نویسد، و همین است که باعث می‌شود پاسخ داخل گفت‌وگو بنشیند نه کنارش.
draftIdString
محتوای یک پیش‌نویس ذخیره‌شده را زیر این پاکت بفرستید، حداکثر 256 نویسه. گیرندگان، موضوع و سرآیندهایی که اینجا ساخته می‌شوند همان‌هایی‌اند که روی سیم می‌روند.
templateHash
یک قالب ذخیره‌شده را سمت سرور رندر می‌کند، با شناسه (`tpl_…`) یا slug، که `version` یک نسخه را سنجاق می‌کند و `props` و `slots` آن را پر می‌کنند. یک بار و هنگام پذیرفته‌شدن مورد تعیین می‌شود، و در کنار `html` یا `text` و در کنار `draftId` رد می‌شود، چون هرکدام از آن‌ها پاسخی دوم به این پرسش است که پیام چه چیزی دارد.
scheduledAtTime, DateTime or String
یک Time یا DateTime، یک لحظهٔ ISO 8601، یا مدتی مانند `PT1H`، دست‌کم یک ثانیه در آینده و حداکثر 365 روز بعد. یک Date در Ruby یعنی نیمه‌شب UTC همان روز. موردها مستقل از هم زمان‌بندی می‌شوند، پس یک دسته می‌تواند صد زمان ارسال متفاوت داشته باشد.
cancellableForSecondsInteger
پنجرهٔ لغو برحسب ثانیه روی یک ارسال فوری، از 0 تا 900 با پیش‌فرض 0. هر مقدار بالای 0 در کنار `scheduledAt` روی همان مورد رد می‌شود، چون پیام زمان‌بندی‌شده تا وقتی نرفته از پیش قابل لغو است.
trackingHash
`opens` و `clicks`، که هرکدام اختیاری‌اند و هرکدام تنظیم را فقط برای همین یک پیام بازنویسی می‌کنند. کلیدی که ننویسید از نشانی‌ای که پیام از آن فرستاده می‌شود (یا catch-allی که آن را گرفته) پیروی می‌کند، که خاموش است مگر همان نشانی روشنش کرده باشد.
tagsHash
حداکثر 10 برچسب، با کلیدهای 1 تا 64 نویسه از میان `A-Za-z0-9_-` و مقدارهای تا 256 نویسه. روی پیام همان‌طور بازگردانده می‌شوند و هرگز تفسیر نمی‌شوند: `emails.list` فقط بر اساس `status:`، `from:`، `broadcast_id:` و بازهٔ زمان‌بندی فیلتر می‌کند و بس، پس برچسب چیزی است که از روی پیامی که از پیش در دست دارید بخوانید، نه راهی برای یافتنش.
translateHash
این مورد را به زبانی دیگر بفرستید، که در زمان پذیرش تعیین می‌شود تا کلماتی که تأیید شده‌اند همان کلماتی باشند که بیرون می‌روند. حداکثر 10 مورد در یک دسته می‌توانند آن را داشته باشند: هرکدام چند فراخوانی مدل خرج می‌کند و موردها به ترتیب اجرا می‌شوند، پس دسته‌ای بزرگ‌تر وسط ارسال قطع می‌شد. بیش از آن، کل فراخوانی با `too_many_items` روی `emails` رد می‌شود، پیش از آنکه چیزی فرستاده شود.

پاسخ: OpenEmail::BatchResult

itemsArray<Hash>
یک Hash برای هر پیام، به همان ترتیبی که فرستاده‌اید. هیچ‌چیز بازگردانده نمی‌شود، پس این سابقهٔ آن است که بر سر هر پیام چه آمد، نه گزارشی دربارهٔ یک تراکنش. API چه همهٔ پیام‌ها پذیرفته شوند، چه بعضی و چه هیچ‌کدام، 207 پاسخ می‌دهد، پس فراخوانی در هر حال برمی‌گردد و آنچه باید رویش شاخه بزنید `status` هر مورد است.
sentInteger
چند مورد «پذیرفته» شده‌اند، که همان تعداد موردهایی نیست که رفته‌اند. یک مورد می‌تواند `ok` باشد و باز هم `email`ای داشته باشد که `status` آن `failed` یا `partial` است، چون لایهٔ انتقالی که پس از ساخته شدن ردیف پیام را رد کند، نتیجه‌ای از تحویل است نه درخواستی ردشده.
failedInteger
چند مورد `error` دارند. شمار بالاتر از 0 فهرستی برای اقدام است نه دلیلی برای فرستادن دوبارهٔ دسته. پیام‌های پذیرفته‌شده پیش از این رفته‌اند.

هر مورد

indexInteger
جایگاهی که پیامِ این مورد در Array فرستاده‌شده داشت. علاوه بر ترتیب، به شکل یک کلید هم حمل می‌شود، تا کدی که `items` را فیلتر یا مرتب می‌کند باز هم بتواند بگوید کدام پیام شکست خورده است.
statusString
`ok` یا `error`. `ok` حامل `email` است، `error` حامل `error`، و هیچ موردی هر دو را ندارد.
emailHash
پیام پذیرفته‌شده، فقط روی مورد `ok`، با همان شکلی که یک ارسال تکی برمی‌گرداند. `replayed` آن وقتی true است که `Idempotency-Key` مشتق‌شده با ارسالی که از پیش وجود داشته مطابقت کرده باشد، پس چیز تازه‌ای فرستاده نشده و این همان پیام اصلی است. کلید `tracking` ندارد، چون تعامل بعداً گزارش می‌شود و در زمان پذیرش چیزی برای گزارش نیست.
errorHash
چرا همین یک پیام رد شد، فقط روی مورد `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` خودش رفتار کنید.
messageString
یک جملهٔ نوشته‌شده برای آدم، که مقدار خطاساز را در جایی که وجود دارد نام می‌برد. شناسه‌ای پایدار نیست. روی `code` شاخه بزنید.
paramString
فیلدی که رد شد، به شکل مسیری نقطه‌دار درون همان یک پیام: `to.0`، `from`، `attachments`. وقتی شکست هیچ فیلدی را نام نبرد غایب است، و هرگز با جایگاه مورد در دسته پیشوند نمی‌گیرد؛ آن کارِ `index` است.