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

ارسال یک ایمیل

POST /emails: یک پیام، حالا یا بعداً.

POSTapi.openemail.uk/emails

فراخوانی واقعی را با کلید خودتان روی فضای کاری شما اجرا می‌کند.

درخواست

from الزامی است. برخلاف نگارشگر، فرستندهٔ جایگزینی در کار نیست، چون آن جایگزین نشانی پیش‌فرض فضای کاری است و با آمدن و رفتن نشانی‌ها نامرئی عوض می‌شود.

فیلدالزامیتوضیحات
fromبلهیک نشانی خالی یا Name <addr>. باید یکی باشد که کلید اجازهٔ ارسال با آن را دارد.
toبلهتا ۵۰ گیرنده روی to، cc و bcc روی هم.
cc, bccخیرنام گیرنده‌های bcc هرگز در بایت‌هایی که دیگران دریافت می‌کنند نمی‌آید.
subjectخیرپیش‌فرضش خالی است.
html, textیکی از این دوهر دو هم اشکالی ندارد. آنچه گیرنده‌ها می‌بینند HTML است.
templateیکی از این‌ها{ id, version?, props?, slots? }. یک بدنهٔ ذخیره‌شده، با شناسه یا با slug. در کنار html، text یا draftId رد می‌شود. به «ارسال با یک قالب» نگاه کنید.
replyToخیریک نشانی یگانه.
headersخیرX-*، List-*، Reply-To، Precedence، Auto-Submitted، Importance، Priority، Feedback-Id.
attachmentsخیر{ filename, content, contentType } به شکل base64، در مجموع ۵ مگابایت، یا { fileId } که فایلی را نام می‌برد که از پیش در فضای کاری است. ۲۰ فایل.
attachmentDeliveryخیرmime، link یا auto. auto وقتی فایل‌ها روی دامنه‌ای با دامنهٔ فایل‌های فعال از ۲ مگابایت بگذرند آن‌ها را پیوند می‌کند. پیش‌فرضش تنظیم صندوق پستی است.
threadIdخیرپاسخ در یک رشتهٔ موجود.
draftIdخیرارسال یک پیش‌نویس موجود.
scheduledAtخیرلحظه یا مدت به شکل ISO. به زمان‌بندی نگاه کنید.
cancellableForSecondsخیرپنجرهٔ لغوی از ۰ تا ۹۰۰ ثانیه روی یک ارسال فوری. در کنار scheduledAt رد می‌شود، چون آن تا لحظهٔ ارسال لغو‌شدنی می‌ماند. به زمان‌بندی نگاه کنید.
signatureخیرfalse امضا را از این پیام برمی‌دارد. در غیر این صورت پیام امضای نشانی‌ای را که از آن فرستاده می‌شود با خود دارد، که یا امضای خودِ آن نشانی است یا امضایی که برای All addresses تنظیم شده.
tagsخیرتا ۱۰ برچسب از آنِ خودتان. بازتاب داده می‌شوند و هرگز تفسیر نمی‌شوند.
trackingخیر{ opens?, clicks? }. هرکدام تنظیم را برای این پیام بازنویسی می‌کند؛ فیلدی را جا بگذارید و آن نیمه به تنظیم نشانی‌ای که از آن فرستاده می‌شود برمی‌گردد، وگرنه به All addresses، و روشن است مگر آنکه یکی از آن دو خاموشش کرده باشد.
translateخیر{ to, from?, subject?, includeOriginal? }. پیام را به زبان گیرنده می‌فرستد. در لحظهٔ پذیرش درخواست حل می‌شود، و در کنار draftId رد می‌شود.

فیلدهای ناشناخته به‌جای نادیده‌گرفته‌شدن رد می‌شوند، پس نامی که غلط تایپ شده همین حالا 422 می‌گیرد به‌جای آنکه بعداً غافلگیرتان کند. هدرهایی که مجوز فرستنده را از کار می‌اندازند (From، Sender، Bcc، Message-ID، Return-Path و دیگران) با reserved_header رد می‌شوند.

پاسخ

وقتی پیام از پیش رفته باشد 200، و وقتی هنوز باید چیزی بر سرش بیاید 202. فراخوانی که روی کد وضعیت شاخه می‌زند دربارهٔ هر دو درست می‌گوید.

200 OK
{  "object": "email",  "id": "msg_c5f21cc6bfec4e848caf905b",  "status": "sent",  "mode": "live",  "from": "[email protected]",  "subject": "Your September invoice",  "messageId": "<2598…@acme.com>",  "transport": "ses",  "sentAt": "2026-08-29T08:19:08.000Z",  "source": "api",  "replayed": false}

id دستگیرهٔ پایداری است که نگه می‌دارید، و همان که یک رویداد تحویل روی آن برمی‌گردد، چون webhook یک bounce آن را emailId می‌نامد. messageId همان Message-ID مطابق RFC 5322 است و تا وقتی MIME وجود نداشته باشد null است. با آن همبسته‌سازی نکنید: سرویس ارسال آن هدر را در راه خروج بازنویسی می‌کند، پس مقدار اینجا در هیچ گزارش bounce یا تحویلی پیدا نمی‌شود و تطبیق روی آن هرگز شلیک نمی‌کند.

به زبان گیرنده

translate پیام را پیش از رفتنش به زبان کسی دیگر می‌نویسد. بدنه، و موضوع مگر آنکه آن را خاموش کنید، در همان لحظه‌ای ترجمه می‌شود که درخواست پذیرفته می‌شود، که همان قاعده‌ای است که template از آن پیروی می‌کند و به همان دلایل باربر است: پیام زمان‌بندی‌شده واژه‌هایی را با خود دارد که تأیید شده‌اند، نه هر چیزی که یک مدل در روز سه‌شنبه تولید می‌کند، و ترجمه‌ای که نتوانسته تولید شود ارسال را پیش از آنکه ردیفی وجود داشته باشد رد می‌کند. هیچ چیزی به زبانی که فرستنده‌اش انتخاب نکرده تحویل نمی‌شود.

translate

tostringالزامی
زبانی که باید به آن نوشته شود: یک کد BCP-47 (`de`)، یک نام انگلیسی («German») یا نام خودِ زبان («Deutsch»)، ۲ تا ۶۰ نویسه. هر سه پیش از هر چیز دیگری به کد جدول عادی‌سازی می‌شوند، پس یک درخواست‌اند، و این مهم است چون اثرانگشت Idempotency-Key روی درخواستِ تجزیه‌شده گرفته می‌شود. نام‌های دیگر هم حل می‌شوند: `zh-TW` به `zh-Hant` بدل می‌شود. آنچه به هیچ چیز حل نشود روی `translate.to` یک 422 است.
fromstring
زبانی که پیام را به آن نوشته‌اید، در هر یک از همان سه شکل. صرفاً یک بهینه‌سازی است. اگر جا بگذاریدش، بدنه خوانده می‌شود و زبان درمی‌آید، که هزینه‌اش یک فراخوانی کوتاه مدل است. روی مسیری پرحجم ارزش گفتن دارد، و وقتی بدنه بیشتر نام و عدد و پیوند است هم ارزش گفتن دارد: تشخیص به‌جای حدس‌زدن خودداری می‌کند، و مبدأ نامعلوم چیزی جز نام زبان در زیرنویسِ بالای متن اصلی شما برایتان هزینه ندارد. این همان `from` سطح بالا نیست، که یک نشانی است.
subjectboolean
خط موضوع را هم ترجمه کن. پیش‌فرضش true است؛ false موضوع را دقیقاً همان‌طور که نوشته‌اید می‌فرستد.
includeOriginalboolean
آنچه را واقعاً نوشته‌اید زیر ترجمه بگذار، پشت یک جداکننده و با زیرنویسی به زبان گیرنده. پیش‌فرضش true است و ارزش روشن‌ماندن دارد. تنها چیزی است که به کسی که می‌خواند اجازه می‌دهد جمله‌ای را که عجیب می‌نشیند وارسی کند، به‌جای آنکه از او خواسته شود به مدلی اعتماد کند که هیچ‌کدامتان خروجی‌اش را نمی‌بینید.
curl
curl -X POST "$OE/emails" -H "$AUTH" -H "Content-Type: application/json" \  -d '{    "from": "[email protected]",    "to": ["[email protected]"],    "subject": "Your September invoice",    "html": "<p>Invoice attached. Payment is due on the 14th.</p>",    "translate": { "to": "de" }  }'
200 OK
{  "object": "email",  "id": "msg_c5f21cc6bfec4e848caf905b",  "status": "sent",  "from": "[email protected]",  "subject": "Ihre Rechnung für September",  "translation": {    "language": "de",    "languageName": "German",    "detectedSourceLanguage": "en",    "subject": true,    "includeOriginal": true  }}

translation افزودنی است و تنها روی پیامی پیدا می‌شود که ترجمه شده: روی این پاسخ و روی GET /emails/{id}، و هرگز روی ردیف فهرست، چون فهرست درخواست ذخیره‌شده را نمی‌گیرد و سکوتش آنجا هیچ چیزی در هیچ جهتی نمی‌گوید. به‌جای ردیف‌های کامل زبان، کد با خود دارد: سندِ کاری است که انجام شده، و GET /languages جایی است که نام بومی زندگی می‌کند. subject روی پاسخ همان موضوع ترجمه‌شده است، پس یک کنسول هرگز پیامی را زیر رشته‌ای فهرست نمی‌کند که گیرنده هرگز ندیده است.

  • با template کار می‌کند، و همین حالت سودمند است: آنچه ترجمه می‌شود خروجی رندرشده است، پس یک بدنهٔ ذخیره‌شده به هر زبانی که مشتریانتان می‌خوانند خدمت می‌کند. قالبی که یک سند کامل رندر می‌کند اول از هم باز می‌شود: تنها آنچه درون <body> است به مدل می‌رسد، و doctype، بلوک‌های <style> و قاعده‌های @font-face دوباره گرد پاسخ گذاشته می‌شوند. به همین دلیل هم هست که محدودیت ۳۰٬۰۰۰ نویسه‌ای متن را اندازه می‌گیرد نه سند را: پیام دوخطی‌ای که در یک شیوه‌نامهٔ برندشده پیچیده شده، یک پیام دوخطی است.
  • تنها بخشی از یک قالب که ترجمه نمی‌شود <title> آن است، که هیچ کلاینت ایمیلی نشانش نمی‌دهد. یک <Preview> در react-email درون بدنه رندر می‌شود و با بقیه ترجمه می‌شود.
  • در کنار draftId رد می‌شود: یک 422 روی translate، با این متن: «پیش‌نویس همان‌طور که نوشته شده فرستاده می‌شود؛ یا بدنه‌ای را ترجمه کنید یا پیش‌نویسی را بفرستید، نه هر دو». پیش‌نویس را یک آدم نوشته و همان‌طور که رهایش کرده فرستاده می‌شود.
  • عمداً بخشی از اثرانگشت ایدمپوتنسی نیست. آنچه هش می‌شود درخواستی است که فرستاده‌اید، شاملِ translate؛ آنچه مدل تولید کرده نه. پس تلاش دوباره برای ارسالی که پاسخ نگرفته با همان Idempotency-Key پاسخ اصلی را بازپخش می‌کند. پیامی که از پیش هست برمی‌گردد، بدون ارسال دوم و بدون ترجمهٔ دوم. هش‌کردن خودِ واژه‌ها به‌جای این، یک تلاش دوبارهٔ صادقانه را هر بار با اثرانگشتی متفاوت می‌ساخت، و همین است که یک پیام را دو بار بیرون می‌فرستد.
  • پیام ترجمه‌شده‌ای که در صف یا زمان‌بندی‌شده است در برابر تغییر واژه‌ها منجمد می‌شود. جابه‌جایش کنید یا لغوش کنید؛ عوض‌کردن آنچه می‌گوید یعنی لغو و ارسال دوباره، پیش روی کسی که بتواند واژه‌های تازه را بخواند.
  • مقصدی که راست‌به‌چپ است راست‌به‌چپ تولید می‌شود: ترجمه در dir="rtl" پیچیده می‌شود و متن اصلی شما زیر آن با جهت خودش می‌آید. این ویژگی از پالایشگر خروجی جان سالم به در می‌برد، که دقیقاً به همین دلیل dir را مجاز می‌داند، پس پیامی که روی سیم می‌رود همان جهتی را با خود دارد که پیش‌نمایش نشان داد.
کدوضعیتچه زمانی
`invalid_parameter`422translate.to یا translate.from زبانی را نام می‌برد که نمی‌توانیم جایش بدهیم. پیام می‌گوید کدام سه شکل پذیرفته می‌شوند و به GET /languages اشاره می‌کند.
`unknown_language`422همان شکست، یک گام دیرتر گرفته شده، به دست سرویس به‌جای schema. یک پشتیبان، روی translate.to.
`translation_too_long`422بیش از ۳۰٬۰۰۰ نویسه در هر یک از دو سرِ فراخوانی مدل. رد‌کردن به‌جای بریدن: نیمی از یک پیام ترجمه‌شده هیچ درزی ندارد که نشان دهد کجا ایستاده، و کسی که می‌خواند بر پایهٔ همان نیمی که گرفته عمل می‌کند.
`translation_not_configured`409فضای کاری کلید هوش مصنوعی ندارد و هوش مصنوعی پلتفرم خاموش است. یک 409 به‌جای 503، چون تلاش دوباره هم دقیقاً همان‌طور شکست می‌خورد. چیزی فرستاده نشد. اگر می‌خواستید پیام را همان‌طور که نوشته‌اید بفرستید، بدون translate بفرستید.
`translation_failed`503ارائه‌دهنده پاسخ نداد، یا چیزی داد که به‌کار نمی‌آمد. چیزی فرستاده نشد؛ پیام هرگز به‌عنوان جایگزین، ترجمه‌نشده پست نمی‌شود. این یکی از آنِ ماست و ارزش تلاش دوباره دارد.
`unknown_parameter`422کلیدی ناشناخته درون translate، که مانند بقیهٔ درخواست یک شیء سخت‌گیر است.

در ارسالی که از کد می‌آید کسی نیست که اول ترجمه را بخواند. POST /emails/translate همان رفت‌وبرگشت است که یک گام زودتر متوقف شده، برای نشان‌دادن به کسی که می‌خواهد چیزی بفرستد. سپس آنچه را او تأیید کرده به‌عنوان یک html/subject معمولی بفرستید، بدون آنکه اصلاً translate روی درخواست باشد.