メールを送信する
`emails.send`: 1 通のメッセージを、今またはあとで送ります。
emails.send
from openemail import openemail email = 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': pdf_bytes}], 'threadId': 'thread_…', 'scheduledAt': 'PT1H', 'tags': {'order': '4021'}, 'tracking': {'opens': True, 'clicks': True},})to、cc、bcc は受信者を 1 人でも複数人でも受け取り、1 人の場合は代わりに配列へ包みます。それぞれ素のアドレス、Name <addr@host>、{'email': ..., 'name': ...} のいずれでも構いません。
パラメーター
fromRecipientInput必須- 差出人。アドレス単体、`Name <addr@host>`、または dict。このキーが差出人として使えるものである必要があります。代替の差出人はないため、送信は常に送信元のアドレスを明示します。
toRecipientInput | list[RecipientInput]必須- 受信者は 1 人でも複数人でも指定でき、1 人の場合は代わりに包みます。to、cc、bcc の合計で最大 50 人です。
ccRecipientInput | list[RecipientInput]- 50 人の受信者上限に数えられます。
bccRecipientInput | list[RecipientInput]- 他の誰かが受け取るバイト列の中に現れることはありません。受信者ごとに 1 通ずつエンベロープが送出されるからです。
replyToRecipientInput- 単一のアドレスで、Reply-To ヘッダーとして送られます。
subjectstr- RFC 5322 の行長制限である最大 998 文字。既定は空です。
htmlstr- html、text、draftId、template のいずれか 1 つが必要です。html と text の両方が与えられたとき、受信者が見るのは HTML です。
textstr- プレーンテキストのパートです。
templateEmailSendTemplate- 保存されたテンプレートをサーバー側でレンダリングします。`version` は固定用で、省略するとリクエストが受け付けられた時点で公開されているものが使われます。未知の prop や欠けている prop は、メッセージ内の空白ではなく 422 になります。
draftIdstr- 保存済みの下書きをこのエンベロープで送ります。
headersdict[str, str]- `X-*`、`List-*`、Reply-To、Precedence、Auto-Submitted、Importance、Priority、Feedback-Id です。トランスポート自身が設定するものは、黙って落とされるのではなく拒否されます。
attachmentslist[AttachmentInput]- 省略可能な `'contentType'` を伴う `{'filename': ..., 'content': ...}`、またはワークスペースにすでにあるファイル(`files.upload` でアップロードしたものなど)を指す `{'fileId': ...}` です。content にバイト列を渡せば base64 にエンコードされます。20 ファイルまでで、インラインのファイルはデコード後の合計 5 MB が上限です。保存済みファイルはそれより大きくてもよく、ダウンロードリンクとして送られます。
attachmentDeliveryAttachmentDeliveryMode- `mime`、`link`、`auto` のいずれか。`auto` は、files ドメインが有効なドメインでファイルが 2 MB を超えるとダウンロードリンクとして送り、それ以外はメッセージ内に入れます。省略するとメールボックスの設定が適用され、その既定は `auto` です。
threadIdstr- 既存のスレッドへ返信します。トランスポートが In-Reply-To と References を書き込みます。
scheduledAtdatetime | str- `datetime`、ISO-8601 の時刻、または `PT1H` のような期間。最大 1 年先までで、過去は指定できません。cancellableForSeconds とは併用できません。
cancellableForSecondsint- 0 から 900。即時送信における取り消し猶予で、コンポーザーの取り消し機構をハードコードせずに公開したものです。
tagsdict[str, str]- 最大 10 個のラベル。そのまま返り、絞り込みにも使えます。解釈されることはありません。
signaturebool- このメッセージに送信元アドレスの署名を付けるかどうか。そのアドレス自身の署名、なければキャッチオールが受け止めたアドレスならキャッチオールの署名、それもなければ OpenEmail フッター(そのアドレスでオフにしていない場合)です。省略すると、`html` 本文は書いたとおりに署名なしで送られ、`text` だけの本文には署名が付きます。領収書、パスワード再設定、ダイジェストなど、プログラムが誰かの代わりに送るメールには `False` を指定してください。いずれも、人の署名を下に付けたいものではありません。
trackingTrackingRequest- このメッセージに開封ピクセルを付け、リンクを書き換えるかどうか。送信元アドレス(またはそのアドレスを受け止めたキャッチオール)についてトラッキングがオンにされていない限りオフです。ここでどちらかを明示すれば、アドレスの設定がどうであれ、そのメッセージについては指定どおりになります。
translateSendTranslateOptions- 受信者の言語で送ります。`to` は言語コード、英語名、その言語自身の名前を受け取り、`subject` と `includeOriginal` はどちらも既定で true です。リクエストが受け付けられた時点で解決されるので、予約されたメッセージは承認された言葉を運びます。`draftId` とは併用できません。
レスポンス
idstr- 送信 id、`msg_…` です。`get`、`cancel`、`reschedule`、`get_tracking` に使います。
statusEmailStatus- queued、scheduled、sending、sent、partial、bounced、cancelled、failed のいずれか。呼び出しが戻ったという事実ではなく、これを読んでください。`partial` は独立した状態です。一部の受信者にはすでに届いており取り消せないので、再送は誤りであり、失敗と報告するのは嘘になります。
modeApiKeyMode- どの種類のキーが送ったか。テスト送信は記録され、決して送出されません。
fromstr- 実際に認可され通信に載ったアドレスで、求められたものとは限りません。
subjectstr | None- 送られたとおりです。
messageIdstr | None- RFC 5322 の Message-ID。MIME ができるまでは null です。送信サービスが送出時にヘッダーを書き換えるので、バウンスや配送報告がこの値を運ぶことはありません。イベントが返ってくるのは `id` です。
threadIdstr | None- 着地したスレッドです。
transportEmailTransport | str | None- メッセージがどう出ていったか。送出までは null です。
attemptsint- 送出が何回試みられたか。
lastErrorstr | None- 直近の試行が失敗した理由を、そのまま記したものです。
scheduledAtstr | None- 送信予定の ISO 時刻です。
cancellableUntilstr | None- 現在時刻がこれより前である限り、取り消しは有効です。
sentAtstr | None- 出ていった ISO 時刻です。
tagsdict[str, str]- 送ったものが、そのまま返ります。
sourceEmailSource | str- composer、api、mcp、ai、oauth、form のいずれか。どの面が要求したかを示します。API キーを使うこのクライアントは `api`、アクセストークンを使うこのクライアントは `oauth` です。
createdAtstr- 記録が書かれた ISO 時刻です。
replayedbool- Idempotency-Key がすでに存在する送信と一致したときに true になります。新しく送られたものはなく、これは元のメッセージです。
translationNotRequired[EmailTranslationResource]- 翻訳されたメッセージにのみ、かつ保存されたリクエスト全体を運ぶ場所、つまりこのレスポンスと `get` にのみ存在します。`language`、`languageName`、`detectedSourceLanguage`、`subject`、`includeOriginal` を持つ dict で、いずれも言語行ではなくコードです。一覧の行には決して現れないので、そこにないことはどちらの意味にもなりません。`email.get('translation')` で読み取ってください。
受信者の言語で送る
translate は、メッセージが出ていく前にそれを別の人の言語で書き直します。本文と、無効にしない限り件名が、API がリクエストを受け付けた時点で翻訳され、その出力がそのまま送られます。翻訳を生成できなかった場合は、あなたが書いた言語のまま投函するのではなく送信を拒否します。
from openemail import openemail email = 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'},}) print(email.get('translation'))それが出ていく前に、誰もそれを読んでいません。emails.translate は、同じ往復を 1 段階手前で止めたものです。人に見せ、手直しさせ、承認されたものを、呼び出しに translate をまったく付けずに送ってください。もう一度渡せば二重に翻訳され、その人の編集は捨てられます。
from openemail import openemail preview = openemail.emails.translate({ 'subject': 'Your September invoice', 'html': '<p>Invoice attached. Payment is due on the 14th.</p>', 'to': 'de',}) print(preview['language']['native'], preview['detectedSourceLanguage'])print(preview['html']) approved_subject = input(f"Subject [{preview['subject']}]: ") or preview['subject'] or '' openemail.emails.send({ 'from': '[email protected]', 'to': '[email protected]', 'subject': approved_subject, 'html': preview['html'] or '',})from openemail import LANGUAGES, is_rtl_language, language_by_code, openemail, resolve_language current = openemail.languages.list() german = resolve_language('Deutsch')traditional = resolve_language('zh-TW')upper = language_by_code('DE') assert len(LANGUAGES) == 200assert german is not None and german['code'] == 'de'assert traditional is not None and traditional['code'] == 'zh-Hant'assert upper is not None and upper['native'] == 'Deutsch'assert is_rtl_language('ar')この表はピッカーの並び順でバンドルされているので、最初のリクエストの前にピッカーを埋められます。languages.list() は、このバージョンに同梱されたものではなく最新のものが欲しい呼び出し側のために、通信上の同じ行をプレーンなリストとして返します。resolve_language はコード、英語名、その言語自身の名前、エイリアス(zh-TW は、もう掲載されていないコードのエイリアスです)を受け取り、language_by_code は大文字小文字を区別せずコードに完全一致し、16 行は右から左へ書く言語です。native、label、code をまとめて検索し、native を先に表示し、コードを保存してください。
emails.translate は自動的に再試行されません。モデル呼び出しを消費し、何も書き込まないので、冪等にすべきものがなく、応答のなかったリクエストの再試行は同じ答えを 2 度買うだけです。
- 何にも解決しない言語は、何かが送られる前に
translate.toに対するvalidation_errorになります。 - 30,000 文字を超えると
translation_too_long、インストールに AI が設定されていなければtranslation_not_configured、ワークスペースがその日の AI アクションを使い切っていれば 429 のai_quota_exceeded(UTC の 0 時にリセットされ、再試行はされません)、プロバイダーが応答しなければtranslation_failedです。いずれも、フォールバックとして未翻訳のメッセージを送ることはありません。 templateと併用できます。翻訳されるのはレンダリング後の出力なので、保存された 1 つの本文が、顧客が読むすべての言語に対応できます。文書全体をレンダリングするテンプレートは、doctype、<style>ブロック、@font-face規則を保ちます。モデルへ渡されるのは body だけで、残りは後から周りに戻されます。<title>はそのままで、どのみち何も表示しません。- 再試行に追加費用はかかりません。翻訳は冪等性のフィンガープリントの一部ではなく(リクエストは
translateを含めて一部です)、応答のなかった送信を同じIdempotency-Keyで再試行すると、翻訳して 2 通目を送るのではなく、すでに存在するメッセージを再生します。 - queued または scheduled の翻訳済みメッセージは、文面の変更に対して凍結されます。
emails.rescheduleは依然として時刻を動かせますが、内容を変えたいときは取り消して送り直すことになります。
添付ファイル
content は通信上 base64 です。バイト列を渡せば代わりにエンコードされます。
from pathlib import Path from openemail.types import AttachmentInput attachments: list[AttachmentInput] = [ { 'filename': 'invoice.pdf', 'content': Path('invoice.pdf').read_bytes(), 'contentType': 'application/pdf', },]ほかの場所で必要なら、エクスポートされている to_base64 を使えます。content に入れた str はそのまま送られるので、すでに base64 になっている必要があります。