ドキュメント本文へスキップ
SDK

メールを送信する

`emails.send`: 1 通のメッセージを、今またはあとで送ります。

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 },})

toccbcc は受信者を 1 人でも複数人でも受け取り、1 人の場合は代わりに配列へ包みます。それぞれ素のアドレス、Name <addr@host>{ email, name } のいずれでも構いません。

パラメーター

fromRecipientInput必須
差出人。アドレス単体、`Name <addr@host>`、またはオブジェクト。このキーが差出人として使えるものである必要があります。代替の差出人はありません。代替になるのはワークスペースの既定アドレスで、アドレスが増減するたびに変わってしまうからです。
toRecipientInput | RecipientInput[]必須
受信者は 1 人でも複数人でも指定でき、1 人の場合は代わりに包みます。to、cc、bcc の合計で最大 50 人です。
ccRecipientInput | RecipientInput[]
50 人の受信者上限に数えられます。
bccRecipientInput | RecipientInput[]
他の誰かが受け取るバイト列の中に現れることはありません。受信者ごとに 1 通ずつエンベロープが送出されるからです。
replyToRecipientInput
単一のアドレスで、Reply-To ヘッダーとして送られます。
subjectstring
RFC 5322 の行長制限である最大 998 文字。既定は空です。
htmlstring
html、text、draftId、template のいずれか 1 つが必要です。html と text の両方が与えられたとき、受信者が見るのは HTML です。
textstring
プレーンテキストのパートです。
template{ id, version?, props?, slots? }
保存されたテンプレートをサーバー側でレンダリングします。`version` は固定用で、省略するとリクエストが受け付けられた時点で公開されているものが使われます。未知の prop や欠けている prop は、メッセージ内の空白ではなく 422 になります。
draftIdstring
保存済みの下書きをこのエンベロープで送ります。
headersRecord<string, string>
`X-*`、`List-*`、Reply-To、Precedence、Auto-Submitted、Importance、Priority、Feedback-Id です。トランスポート自身が設定するものは、黙って落とされるのではなく拒否されます。
attachmentsAttachmentInput[]
`{ filename, content, contentType? }`、またはワークスペースにすでにあるファイルを指す `{ fileId }` です。content にバイト列を渡せば base64 にエンコードされます。20 ファイルまでで、インラインのファイルはデコード後の合計 5 MB が上限です。保存済みファイルはそれより大きくてもよく、ダウンロードリンクとして送られます。
attachmentDeliveryAttachmentDeliveryMode
`mime`、`link`、`auto` のいずれか。`auto` は、files ドメインが有効なドメインでファイルが 2 MB を超えるとダウンロードリンクとして送り、それ以外はメッセージ内に入れます。省略するとメールボックスの設定が適用され、その既定は `auto` です。
threadIdstring
既存のスレッドへ返信します。トランスポートが In-Reply-To と References を書き込みます。
scheduledAtDate | string
Date、ISO-8601 の時刻、または `PT1H` のような期間。最大 1 年先までで、過去は指定できません。cancellableForSeconds とは併用できません。
cancellableForSecondsnumber
0 から 900。即時送信における取り消し猶予で、コンポーザーの取り消し機構をハードコードせずに公開したものです。
tagsRecord<string, string>
最大 10 個のラベル。そのまま返り、絞り込みにも使えます。解釈されることはありません。
signatureboolean
このメッセージが、送信元アドレスの署名、つまりそのアドレス自身の署名、なければ「すべてのアドレス」に設定された署名を伴うかどうか。既定は true です。署名は、メッセージを送ったクライアントではなくアドレスに属するものだからです。領収書、パスワード再設定、ダイジェストなど、プログラムが誰かの代わりに送るメールには `false` を指定してください。いずれも、人の署名を下に付けたいものではありません。
tracking{ opens?, clicks? }
このメッセージに開封ピクセルを付け、リンクを書き換えるかどうか。ワークスペース所有者が、送信元アドレスまたは「すべてのアドレス」についてトラッキングをオフにしていない限りオンです。ここでどちらかを明示すれば、アドレスの設定がどうであれ、そのメッセージについては指定どおりになります。
translate{ to, from?, subject?, includeOriginal? }
受信者の言語で送ります。`to` は言語コード、英語名、その言語自身の名前を受け取り、`subject` と `includeOriginal` はどちらも既定で true です。リクエストが受け付けられた時点で解決されるので、予約されたメッセージは承認された言葉を運びます。`draftId` とは併用できません。

レスポンス

idstring
送信 id、`msg_…` です。`get`、`cancel`、`reschedule`、`getTracking` に使います。
statusEmailStatus
queued、scheduled、sending、sent、partial、cancelled、failed のいずれか。Promise が解決したという事実ではなく、これを読んでください。`partial` は独立した状態です。一部の受信者にはすでに届いており取り消せないので、再送は誤りであり、失敗と報告するのは嘘になります。
mode'live' | 'test'
どの種類のキーが送ったか。テスト送信は記録され、決して送出されません。
fromstring
実際に認可され通信に載ったアドレスで、求められたものとは限りません。
subjectstring | null
送られたとおりです。
messageIdstring | null
RFC 5322 の Message-ID。MIME ができるまでは null です。送信サービスが送出時にヘッダーを書き換えるので、バウンスや配送報告がこの値を運ぶことはありません。イベントが返ってくるのは `id` です。
threadIdstring | null
着地したスレッドです。
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
Idempotency-Key がすでに存在する送信と一致したときに true になります。新しく送られたものはなく、これは元のメッセージです。
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 は、同じ往復を 1 段階手前で止めたものです。人に見せ、手直しさせ、承認されたものを、呼び出しに 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

この表はピッカーの並び順でバンドルされているので、最初のリクエストの前にピッカーを埋められます。languages.list() は、このバージョンに同梱されたものではなく最新のものが欲しい呼び出し側のために、通信上の同じ行をプレーンな配列として解決します。resolveLanguage はコード、英語名、その言語自身の名前、エイリアス(zh-TW は、もう掲載されていないコードのエイリアスです)を受け取り、languageByCode は大文字小文字を区別せずコードに完全一致し、16 行は右から左へ書く言語です。nativelabelcode をまとめて検索し、native を先に表示し、コードを保存してください。

emails.translate は自動的に再試行されません。モデル呼び出しを消費し、何も書き込まないので、冪等にすべきものがなく、応答のなかったリクエストの再試行は同じ答えを 2 度買うだけです。

  • 何にも解決しない言語は、何かが送られる前に translate.to に対する validation_error になります。
  • 30,000 文字を超えると translation_too_long、インストールに AI が設定されていなければ translation_not_configured、プロバイダーが応答しなければ translation_failed です。いずれも、フォールバックとして未翻訳のメッセージを送ることはありません。
  • template と併用できます。翻訳されるのはレンダリング後の出力なので、保存された 1 つの本文が、顧客が読むすべての言語に対応できます。文書全体をレンダリングするテンプレートは、doctype、<style> ブロック、@font-face 規則を保ちます。モデルへ渡されるのは body だけで、残りは後から周りに戻されます。<title> はそのままで、どのみち何も表示しません。
  • 再試行に追加費用はかかりません。翻訳は冪等性のフィンガープリントの一部ではなく(リクエストは translate を含めて一部です)、応答のなかった送信を同じ Idempotency-Key で再試行すると、翻訳して 2 通目を送るのではなく、すでに存在するメッセージを再生します。
  • queued または scheduled の翻訳済みメッセージは、文面の変更に対して凍結されます。emails.reschedule は依然として時刻を動かせますが、内容を変えたいときは取り消して送り直すことになります。

添付ファイル

content は通信上 base64 です。バイト列を渡せば代わりにエンコードされます。

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

他の場所でも必要なら toBase64 がエクスポートされています。これはチャンク分割しますが、btoa(String.fromCharCode(...bytes)) はしません。あちらはおよそ 100 kB を超えると失敗し、しかもテストしたファイルではなく本番のファイルで失敗します。