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

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

`emails.send`: یک پیام، حالا یا بعداً.

emails.send

send-email.ts
const email = await openemail.emails.send({  from: { email: '[email protected]', name: 'Acme Billing' },  to: ['[email protected]', 'Grace <[email protected]>'],  cc: '[email protected]',  bcc: [{ email: '[email protected]' }],  replyTo: '[email protected]',  subject: 'Your September invoice',  html: '<p>Invoice attached.</p>',  text: 'Invoice attached.',  headers: { 'X-Campaign': 'invoices' },  attachments: [{ filename: 'invoice.pdf', content: pdfBytes }],  threadId: 'thread_…',  scheduledAt: 'PT1H',  tags: { order: '4021' },  tracking: { opens: true, clicks: true },})

to، cc و bcc یک گیرنده یا چند گیرنده می‌گیرند، و اگر یکی تنها بدهید برایتان پیچیده می‌شود. هرکدام می‌تواند نشانی خام، Name <addr@host> یا { email, name } باشد.

پارامترها

fromRecipientInputالزامی
فرستنده. یک نشانی خام، `Name <addr@host>` یا یک object. باید نشانی‌ای باشد که این کلید اجازهٔ ارسال با آن را دارد. هیچ فرستندهٔ جایگزینی وجود ندارد، چون جایگزین همان نشانی پیش‌فرض فضای کاری می‌شد، که با آمدن و رفتن نشانی‌ها عوض می‌شود.
toRecipientInput | RecipientInput[]الزامی
یک گیرنده یا چند گیرنده؛ اگر یکی تنها بدهید برایتان پیچیده می‌شود. حداکثر 50 نشانی روی هم در to، cc و bcc.
ccRecipientInput | RecipientInput[]
به سقف 50 گیرنده شمرده می‌شود.
bccRecipientInput | RecipientInput[]
هرگز در بایت‌هایی که دیگران دریافت می‌کنند نام برده نمی‌شود، چون به ازای هر گیرنده یک پاکت منتقل می‌شود.
replyToRecipientInput
یک نشانی تکی، که به شکل هدر Reply-To فرستاده می‌شود.
subjectstring
حداکثر 998 نویسه، همان حد طول خط RFC 5322. پیش‌فرض: خالی.
htmlstring
یکی از html، text، draftId یا template الزامی است. وقتی هر دو html و text داده شوند، آنچه گیرندگان می‌بینند HTML است.
textstring
بخش متن ساده.
template{ id, version?, props?, slots? }
یک قالب ذخیره‌شده را سمت سرور رندر می‌کند. `version` آن را سنجاق می‌کند؛ ننویسیدش تا هرچه هنگام پذیرش درخواست منتشر شده باشد به کار رود. prop ناشناخته یا نیامده، به‌جای جای خالی در پیام، یک 422 است.
draftIdstring
یک پیش‌نویس ذخیره‌شده را زیر این پاکت بفرستید.
headersRecord<string, string>
`X-*`، `List-*`، Reply-To، Precedence، Auto-Submitted، Importance، Priority و Feedback-Id. هر چیزی که خودِ حمل‌ونقل تنظیم می‌کند به‌جای حذف بی‌سروصدا رد می‌شود.
attachmentsAttachmentInput[]
`{ filename, content, contentType? }`، یا `{ fileId }` که فایلی را نام می‌برد که از پیش در workspace هست. برای content بایت بدهید تا برایتان با base64 رمزگذاری شود. 20 فایل، با سقف مجموع 5 MB برای فایل‌های درون‌خطی پس از رمزگشایی. فایل ذخیره‌شده می‌تواند بزرگ‌تر باشد و به شکل پیوند دانلود می‌رود.
attachmentDeliveryAttachmentDeliveryMode
`mime`، `link` یا `auto`. `auto` فایل‌ها را وقتی روی دامنه‌ای با files domain فعال از 2 MB بگذرند به شکل پیوند دانلود می‌برد و در غیر این صورت درون پیام. اگر ننویسید، تنظیم صندوق اعمال می‌شود، که پیش‌فرضش `auto` است.
threadIdstring
پاسخ‌دادن درون یک thread موجود. حمل‌ونقل In-Reply-To و References را می‌نویسد.
scheduledAtDate | string
یک Date، یک لحظهٔ ISO-8601، یا مدتی مانند `PT1H`. تا یک سال بعد، هرگز در گذشته. نمی‌شود با cancellableForSeconds ترکیبش کرد.
cancellableForSecondsnumber
0 تا 900. پنجرهٔ لغو روی یک ارسال فوری: همان سازوکار لغوِ composer، که به‌جای hardcode شدن در دسترس گذاشته شده است.
tagsRecord<string, string>
تا 10 برچسب، که بازتاب داده می‌شوند و قابل فیلترند. هرگز تفسیر نمی‌شوند.
signatureboolean
اینکه آیا این پیام امضای نشانی‌ای را که از آن فرستاده می‌شود حمل کند یا نه، یعنی امضای خودِ آن نشانی وگرنه امضای تنظیم‌شده برای All addresses. پیش‌فرض true است، چون امضا به نشانی تعلق دارد نه به هر کلاینتی که پیام را فرستاده. برای نامه‌ای که برنامه‌ای از طرف کسی می‌فرستد — رسید، بازنشانی گذرواژه یا خلاصهٔ دوره‌ای — که هیچ‌کدام امضای یک آدم را زیر خود نمی‌خواهند، `false` بگذارید.
tracking{ opens?, clicks? }
اینکه آیا برای این پیام pixel باز شدن افزوده و پیوندها بازنویسی شوند یا نه. روشن است مگر مالک workspace ردیابی را برای نشانی‌ای که پیام از آن می‌رود یا برای All addresses خاموش کرده باشد، و هر یک از این فیلدها که اینجا بیاید تکلیف همان یک پیام را، هرطور که نشانی تنظیم شده باشد، روشن می‌کند.
translate{ to, from?, subject?, includeOriginal? }
آن را به زبان گیرنده بفرستید. `to` یک کد، یک نام انگلیسی یا نام خودِ زبان می‌گیرد؛ `subject` و `includeOriginal` هر دو پیش‌فرض true دارند. هنگام پذیرفته‌شدن درخواست حل می‌شود، پس پیام زمان‌بندی‌شده همان کلماتی را می‌برد که تأیید شده‌اند. در کنار `draftId` رد می‌شود.

پاسخ

idstring
شناسهٔ ارسال، `msg_…`. برای `get`، `cancel`، `reschedule` و `getTracking` از آن استفاده کنید.
statusEmailStatus
queued، scheduled، sending، sent، partial، cancelled یا failed. به‌جای این واقعیت که promise حل شد، همین را بخوانید. `partial` وضعیتی از آنِ خودش است: بعضی گیرندگان آن را دارند و نمی‌شود پس گرفت، پس تلاش دوباره اشتباه است و گزارش شکست دروغ.
mode'live' | 'test'
کدام نوع کلید آن را فرستاده. ارسال در حالت test ثبت می‌شود و هرگز منتقل نمی‌شود.
fromstring
نشانی‌ای که واقعاً مجاز شمرده و روی سیم گذاشته شد، که همیشه همان نشانی درخواست‌شده نیست.
subjectstring | null
همان‌طور که فرستاده شد.
messageIdstring | null
همان Message-ID مربوط به RFC 5322. تا وقتی MIME وجود نداشته باشد null است. سرویس ارسال هدر را در مسیر خروج بازنویسی می‌کند، پس هیچ bounce یا گزارش تحویلی این مقدار را حمل نمی‌کند. چیزی که رویداد با آن برمی‌گردد `id` است.
threadIdstring | null
thread ای که در آن نشست.
transportstring | null
پیام چگونه رفت. تا پیش از ارسال null است.
attemptsnumber
چند بار ارسال تلاش شده است.
lastErrorstring | null
چرا آخرین تلاش شکست خورد، عیناً.
scheduledAtstring | null
لحظهٔ ISO که باید برود.
cancellableUntilstring | null
تا وقتی اکنون پیش از این لحظه است، لغو هنوز کار می‌کند.
sentAtstring | null
لحظهٔ ISO که رفت.
tagsRecord<string, string>
آنچه فرستادید، بازتاب‌داده‌شده.
sourceEmailSource
composer، api، mcp، ai یا queue: کدام سطح درخواست داده است. `api` همین کلاینت است.
createdAtstring
لحظهٔ ISO که رکورد نوشته شد.
replayedboolean
وقتی true است که یک Idempotency-Key با ارسالی که از پیش وجود داشته مطابقت کرده باشد. چیز تازه‌ای فرستاده نشده، و این همان پیام اصلی است.
translationEmailTranslationResource | undefined
فقط روی پیامی حاضر است که ترجمه شده، و تنها جایی که کل درخواست ذخیره‌شده حمل می‌شود: همین پاسخ و `get`. `{ language, languageName, detectedSourceLanguage, subject, includeOriginal }`، همه به شکل کد نه ردیف زبان. ردیف فهرست هرگز آن را ندارد، پس غیابش آنجا به هیچ سمتی چیزی نمی‌گوید.

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

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

translate.ts
const email = await openemail.emails.send({  from: '[email protected]',  to: '[email protected]',  subject: 'Your September invoice',  html: '<p>Invoice attached. Payment is due on the 14th.</p>',  translate: { to: 'de' },}) console.log(email.translation)// { language: 'de', languageName: 'German', detectedSourceLanguage: 'en', subject: true, includeOriginal: true }

کسی پیش از رفتنش آن را نخواند. emails.translate همان رفت‌وبرگشت است که یک گام زودتر متوقف شده. آن را به یک آدم نشان دهید، بگذارید تغییرش دهد، و بعد آنچه را تأیید کرده بدون هیچ translate ای روی فراخوانی بفرستید. دادن دوبارهٔ آن، متن را بار دوم ترجمه می‌کرد و ویرایش‌هایش را دور می‌ریخت.

preview-translation.ts
const preview = await openemail.emails.translate({  subject: 'Your September invoice',  html: '<p>Invoice attached. Payment is due on the 14th.</p>',  to: 'de',}) console.log(preview.language.native, preview.detectedSourceLanguage) const approved = await showToSomebody(preview) await openemail.emails.send({  from: '[email protected]',  to: '[email protected]',  subject: approved.subject,  html: approved.html,})
render-picker.ts
import { LANGUAGES, isRtlLanguage, languageByCode, openemail, resolveLanguage } from '@openemail/sdk' LANGUAGES.length // 200 const current = await openemail.languages.list() resolveLanguage('Deutsch')?.code // 'de'resolveLanguage('zh-TW')?.code // 'zh-Hant'languageByCode('DE')?.native // 'Deutsch'isRtlLanguage('ar') // true

این جدول در بسته هست، به ترتیب picker، پس می‌شود یک picker را پیش از نخستین درخواست پر کرد. languages.list() به همان ردیف‌ها از روی سیم، به شکل آرایه‌ای ساده، حل می‌شود، برای فراخواننده‌ای که ردیف‌های کنونی را به ردیف‌هایی که این نسخه با آن‌ها عرضه شده ترجیح می‌دهد. resolveLanguage یک کد، یک نام انگلیسی، یک نام بومی یا یک نام مستعار می‌گیرد (zh-TW نام مستعار کدی است که دیگر فهرست نمی‌شود)، languageByCode کد را دقیقاً و بدون حساسیت به بزرگی و کوچکی حروف تطبیق می‌دهد، و شانزده ردیف از راست به چپ‌اند. native، label و code را با هم جست‌وجو کنید، native را اول نشان دهید، و کد را ذخیره کنید.

emails.translate به‌طور خودکار دوباره تلاش نمی‌شود. فراخوانی مدل خرج می‌کند و چیزی نمی‌نویسد، پس چیزی برای idempotent کردن نیست و تلاش دوباره پس از درخواستی بی‌پاسخ فقط همان پاسخ را دو بار می‌خرد.

  • زبانی که به هیچ‌چیز حل نشود یک validation_error روی translate.to است، پیش از آنکه چیزی فرستاده شود.
  • translation_too_long برای بیش از 30,000 نویسه، translation_not_configured وقتی نصب هیچ AI پیکربندی‌شده‌ای ندارد، translation_failed وقتی ارائه‌دهنده پاسخ نداده است. هیچ‌کدام به‌عنوان جایگزین، پیام را ترجمه‌نشده نمی‌فرستند.
  • با template کار می‌کند: آنچه ترجمه می‌شود خروجیِ RENDER شده است، پس یک بدنهٔ ذخیره‌شده به هر زبانی که مشتریانتان می‌خوانند خدمت می‌کند. قالبی که یک سند کامل رندر می‌کند، doctype، بلوک‌های <style> و قاعده‌های @font-face خود را نگه می‌دارد: فقط بدنه به مدل می‌رود و بقیه دوباره دورش گذاشته می‌شود. <title> آن دست‌نخورده می‌ماند، که به‌هرحال چیزی نمایشش نمی‌دهد.
  • تلاش دوباره هزینهٔ اضافه ندارد. ترجمه بخشی از اثرانگشت idempotency نیست (خودِ درخواست هست، با translate و همه)، پس بازفرستادن یک ارسال بی‌پاسخ با همان Idempotency-Key پیامی را که از پیش وجود دارد بازپخش می‌کند، نه اینکه بار دوم ترجمه و ارسال کند.
  • پیام ترجمه‌شده‌ای که queued یا scheduled باشد در برابر تغییر واژه‌ها منجمد است. emails.reschedule هنوز جابه‌جایش می‌کند؛ تغییر آنچه می‌گوید یعنی لغو و ارسال دوباره.

پیوست‌ها

content روی سیم base64 است. بایت بدهید تا برایتان رمزگذاری شود.

attachment.ts
attachments: [  { filename: 'invoice.pdf', content: pdfBytes, contentType: 'application/pdf' },]

اگر جای دیگری لازمش داشتید، toBase64 صادر شده است. تکه‌تکه کار می‌کند، کاری که btoa(String.fromCharCode(...bytes)) نمی‌کند. آن یکی روی هر چیزی بیش از حدود 100 kB شکست می‌خورد، و روی فایل واقعی شکست می‌خورد نه روی فایلی که با آن آزمایش کرده‌اید.